From 810ef9117a54f8dfbd362d959d46a2322f86a9d0 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Wed, 19 Jan 2022 11:15:22 +0700 Subject: MDEV-24827: MariaDB 10.5.5 crash (sig 11) during a SELECT Running a query using cursor could lead to a server crash on building a temporary table used for handling the query. For example, the following cursor DECLARE cur1 CURSOR FOR SELECT t2.c1 AS c1 FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1 WHERE EXISTS (SELECT 1 FROM t1 WHERE c2 = -1) ORDER BY c1; declared and executed inside a stored routine could result in server crash on creating a temporary table used for handling the ORDER BY clause. Crash occurred on attempt to create the temporary table's fields based on fields whose data located in a memory root that already freed. It happens inside the function return_zero_rows() where the method Select_materialize::send_result_set_metadata() is invoked for cursor case. This method calls the st_select_lex_unit::get_column_types() in order to get a list of items with types of columns for the temporary table being created. The method st_select_lex_unit::get_column_types() returns first_select()->join->fields in case it is invoked for a cursor. Unfortunately, this memory has been already deallocated bit earlier by calling join->join_free(); inside the function return_zero_rows(). In case the query listed in the example is run in conventional way (without using cursor) the method st_select_lex_unit::get_column_types() returns first_select()->item_list that is not touched by invocation of the method join->join_free() so everything is fine for that. So, to fix the issue the resources allocated for the JOIN class should be released after any activities with the JOIN class has been completed, that is as the last statement before returning from the function return_zero_rows(). This patch includes tests both for the case when a cursor is run explicitly from within a stored routine and for the case when a cursor is opened implicitly as prescribed by the STMT_ATTR_CURSOR_TYPE attribute of binary protocol (the case of prepared statement). --- tests/mysql_client_test.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'tests/mysql_client_test.c') diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index ac9c06ac94b..acd9b61327b 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -19962,6 +19962,58 @@ static void test_mdev_26145() myquery(rc); } +static void test_mdev24827() +{ + int rc; + MYSQL_STMT *stmt; + unsigned long cursor = CURSOR_TYPE_READ_ONLY; + + myheader("test_mdev24827"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT)"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 INT, " + "KEY idx_c2(c2))"); + myquery(rc); + + rc= mysql_query(mysql, "INSERT INTO t1 (c1, c2) " + "SELECT seq, seq FROM seq_1_to_10000"); + myquery(rc); + + rc= mysql_query(mysql, "INSERT INTO t2 (c1, c2) " + "SELECT seq, seq FROM seq_1_to_20000"); + myquery(rc); + + const char* query= + "SELECT t2.c1 AS c1 FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1 " + "WHERE EXISTS (SELECT 1 FROM t1 WHERE c2 = -1) ORDER BY c1"; + + stmt= mysql_stmt_init(mysql); + check_stmt(stmt); + + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + + rc= mysql_query(mysql, "DROP TABLE t2"); + myquery(rc); +} + #ifndef EMBEDDED_LIBRARY #define MDEV19838_MAX_PARAM_COUNT 32 #define MDEV19838_FIELDS_COUNT 17 @@ -20112,6 +20164,7 @@ static void test_mdev19838() #endif // EMBEDDED_LIBRARY static struct my_tests_st my_tests[]= { + { "test_mdev24827", test_mdev24827 }, { "test_mdev_26145", test_mdev_26145 }, { "disable_query_logs", disable_query_logs }, { "test_view_sp_list_fields", test_view_sp_list_fields }, -- cgit v1.2.1 From 7dcef6504620114f503dea92eadbb0e0fe95eb17 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Thu, 20 Jan 2022 16:25:43 +0700 Subject: MDEV-24827: Follow-up patch to fix compilation warning Mixed declarations and code is not allowed for C90 so fix it to avoid compilation break on some platforms. --- tests/mysql_client_test.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'tests/mysql_client_test.c') diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index acd9b61327b..69e451c3019 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -19967,6 +19967,9 @@ static void test_mdev24827() int rc; MYSQL_STMT *stmt; unsigned long cursor = CURSOR_TYPE_READ_ONLY; + const char* query= + "SELECT t2.c1 AS c1 FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1 " + "WHERE EXISTS (SELECT 1 FROM t1 WHERE c2 = -1) ORDER BY c1"; myheader("test_mdev24827"); @@ -19991,10 +19994,6 @@ static void test_mdev24827() "SELECT seq, seq FROM seq_1_to_20000"); myquery(rc); - const char* query= - "SELECT t2.c1 AS c1 FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1 " - "WHERE EXISTS (SELECT 1 FROM t1 WHERE c2 = -1) ORDER BY c1"; - stmt= mysql_stmt_init(mysql); check_stmt(stmt); -- cgit v1.2.1 From f99d141cd26359acd188858a78f3fed607e7a90c Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Sat, 22 Jan 2022 12:46:06 +0700 Subject: MDEV-20516: Assertion `!lex->proc_list.first && !lex->result && !lex->param_list.elements' failed in mysql_create_view Execution of the CREATE VIEW statement sent via binary protocol where the flags of the COM_STMT_EXECUTE request a cursor to be opened before running the statement results in an assert failure. This assert fails since the data member thd->lex->result has not null value pointing to an instance of the class Select_materialize. The data member thd->lex->result is assigned a pointer to the class Select_materialize in the function mysql_open_cursor() that invoked in case the packet COM_STMT_EXECUTE requests a cursor to be opened. After thd->lex->result is assigned a pointer to an instance of the class Select_materialize the function mysql_create_view() is called (indirectly via the function mysql_execute_statement()) and the assert fails. The assert DBUG_ASSERT(!lex->proc_list.first && !lex->result && !lex->param_list.elements); was added by the commit 591c06d4b771124cc2cf453fbf51d5d99a4ad95e. Unfortunately , the condition !lex->result was specified incorrect. It was supposed that the thd->lex->result is set only by parser on handling the clauses SELECT ... INTO but indeed it is also set inside mysql_open_cursor() and that fact was missed by the assert's condition. So, the fix for this issue is to just remove the condition !lex->result from the failing assert. --- tests/mysql_client_test.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'tests/mysql_client_test.c') diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 69e451c3019..db5c9a8688a 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -20013,6 +20013,39 @@ static void test_mdev24827() myquery(rc); } +static void test_mdev_20516() +{ + MYSQL_STMT *stmt; + int rc; + unsigned long cursor= CURSOR_TYPE_READ_ONLY; + + myheader("test_mdev_20516"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE TABLE t1(a INT)"); + myquery(rc); + + const char* query= + "CREATE VIEW v1 AS SELECT * FROM t1"; + + stmt= mysql_stmt_init(mysql); + check_stmt(stmt); + + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); +} + #ifndef EMBEDDED_LIBRARY #define MDEV19838_MAX_PARAM_COUNT 32 #define MDEV19838_FIELDS_COUNT 17 @@ -20163,6 +20196,7 @@ static void test_mdev19838() #endif // EMBEDDED_LIBRARY static struct my_tests_st my_tests[]= { + { "test_mdev_20516", test_mdev_20516 }, { "test_mdev24827", test_mdev24827 }, { "test_mdev_26145", test_mdev_26145 }, { "disable_query_logs", disable_query_logs }, -- cgit v1.2.1 From 2c16fd9bafa1945463dadc9c7bc8f7d455791d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sat, 22 Jan 2022 10:17:05 +0200 Subject: MDEV-24827, MDEV-20516 fixup: Use C90, plug memory leaks --- tests/mysql_client_test.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'tests/mysql_client_test.c') diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index db5c9a8688a..65eb502420d 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -1,5 +1,5 @@ /* Copyright (c) 2002, 2014, Oracle and/or its affiliates. - Copyright (c) 2008, 2020, MariaDB + Copyright (c) 2008, 2022, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20005,6 +20005,7 @@ static void test_mdev24827() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); + mysql_stmt_close(stmt); rc= mysql_query(mysql, "DROP TABLE t1"); myquery(rc); @@ -20018,6 +20019,8 @@ static void test_mdev_20516() MYSQL_STMT *stmt; int rc; unsigned long cursor= CURSOR_TYPE_READ_ONLY; + const char* query= + "CREATE VIEW v1 AS SELECT * FROM t1"; myheader("test_mdev_20516"); @@ -20027,9 +20030,6 @@ static void test_mdev_20516() rc= mysql_query(mysql, "CREATE TABLE t1(a INT)"); myquery(rc); - const char* query= - "CREATE VIEW v1 AS SELECT * FROM t1"; - stmt= mysql_stmt_init(mysql); check_stmt(stmt); @@ -20041,6 +20041,7 @@ static void test_mdev_20516() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); + mysql_stmt_close(stmt); rc= mysql_query(mysql, "DROP TABLE t1"); myquery(rc); -- cgit v1.2.1