summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Shulga <dmitry.shulga@mariadb.com>2022-01-19 11:15:22 +0700
committerDmitry Shulga <dmitry.shulga@mariadb.com>2022-01-19 21:43:32 +0700
commit810ef9117a54f8dfbd362d959d46a2322f86a9d0 (patch)
tree501cf940238da19c33e7d6ecf42095f4cd9b4e30
parent9cd6ecfe501bcbee04d2d203baa81c927664c9e2 (diff)
downloadmariadb-git-810ef9117a54f8dfbd362d959d46a2322f86a9d0.tar.gz
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).
-rw-r--r--mysql-test/r/sp.result28
-rw-r--r--mysql-test/t/mysql_client_test-master.opt1
-rw-r--r--mysql-test/t/mysql_client_test_comp-master.opt1
-rw-r--r--mysql-test/t/sp.test42
-rw-r--r--sql/sql_select.cc4
-rw-r--r--tests/mysql_client_test.c53
6 files changed, 127 insertions, 2 deletions
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index 25675a11f4a..4a905553fba 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -8501,4 +8501,32 @@ b-c
0
drop procedure p1|
drop function f1|
+#
+# MDEV-24827: MariaDB 10.5.5 crash (sig 11) during a SELECT
+#
+CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT);
+CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 INT, KEY idx_c2(c2));
+INSERT INTO t1 (c1, c2) SELECT seq, seq FROM seq_1_to_10000;
+INSERT INTO t2 (c1, c2) SELECT seq, seq FROM seq_1_to_20000;
+CREATE OR REPLACE PROCEDURE p1()
+begin
+DECLARE done INT DEFAULT FALSE;
+DECLARE a INT;
+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;
+DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+OPEN cur1;
+read_loop: LOOP
+FETCH cur1 INTO a;
+IF done THEN
+LEAVE read_loop;
+END IF;
+END LOOP;
+CLOSE cur1;
+END $
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+DROP TABLE t2;
#End of 10.2 tests
diff --git a/mysql-test/t/mysql_client_test-master.opt b/mysql-test/t/mysql_client_test-master.opt
index fcaf2b69fbc..478e62e308f 100644
--- a/mysql-test/t/mysql_client_test-master.opt
+++ b/mysql-test/t/mysql_client_test-master.opt
@@ -2,3 +2,4 @@
--general-log-file=$MYSQLTEST_VARDIR/log/master.log
--log-output=FILE,TABLE
--max-allowed-packet=32000000
+--sequence=on
diff --git a/mysql-test/t/mysql_client_test_comp-master.opt b/mysql-test/t/mysql_client_test_comp-master.opt
index 783093c900b..aa8a3382d09 100644
--- a/mysql-test/t/mysql_client_test_comp-master.opt
+++ b/mysql-test/t/mysql_client_test_comp-master.opt
@@ -1,2 +1,3 @@
--loose-enable-performance-schema
--max-allowed-packet=32000000
+--sequence=on
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 40a1e873ab9..fbd97739a65 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -1,4 +1,6 @@
--source include/have_partition.inc
+--source include/have_sequence.inc
+
#
# Basic stored PROCEDURE tests
#
@@ -10044,5 +10046,45 @@ drop procedure p1|
drop function f1|
delimiter ;|
+--echo #
+--echo # MDEV-24827: MariaDB 10.5.5 crash (sig 11) during a SELECT
+--echo #
+
+CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT);
+CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 INT, KEY idx_c2(c2));
+
+INSERT INTO t1 (c1, c2) SELECT seq, seq FROM seq_1_to_10000;
+INSERT INTO t2 (c1, c2) SELECT seq, seq FROM seq_1_to_20000;
+
+--delimiter $
+
+CREATE OR REPLACE PROCEDURE p1()
+begin
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE a INT;
+
+ 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;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ OPEN cur1;
+ read_loop: LOOP
+ FETCH cur1 INTO a;
+ IF done THEN
+ LEAVE read_loop;
+ END IF;
+ END LOOP;
+ CLOSE cur1;
+END $
+
+--delimiter ;
+
+CALL p1();
+
+DROP PROCEDURE p1;
+DROP TABLE t1;
+DROP TABLE t2;
--echo #End of 10.2 tests
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index a7e2ac4e374..abdc79c1bf3 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -12914,8 +12914,6 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
DBUG_RETURN(0);
}
- join->join_free();
-
if (send_row)
{
/*
@@ -12962,6 +12960,8 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
if (!send_error)
result->send_eof(); // Should be safe
}
+ join->join_free();
+
DBUG_RETURN(0);
}
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 },