Commit a65d01a4 authored by Dmitry Shulga's avatar Dmitry Shulga

MDEV-23182: Server crashes in Item::fix_fields_if_needed /...

MDEV-23182: Server crashes in Item::fix_fields_if_needed / table_value_constr::prepare upon 2nd execution of PS

Repeating execution of a query containing the clause IN with string literals
in environment where the server variable in_predicate_conversion_threshold
is set results in server abnormal termination in case the query is run
as a Prepared Statement and conversion of charsets for string values in the
query are required.

The reason for server abnormal termination is that instances of the class
Item_string created on transforming the IN clause into subquery were created
on runtime memory root that is deallocated on finishing execution of Prepared
statement. On the other hand, references to Items placed on deallocated memory
root still exist in objects of the class table_value_constr. Subsequent running
of the same prepared statement leads to dereferencing of pointers to already
deallocated memory that could lead to undefined behaviour.

To fix the issue the values being pushed into a values list for TVC are created
by cloning their original items. This way the cloned items are allocate on
the PS memroot and as consequences no dangling pointer does more exist.
parent 7bc629a5
......@@ -3100,4 +3100,46 @@ select * from (values (3),(7),(1) union values (2),(4) order by 1 limit 2) as dt
1
2
drop table t1;
#
# MDEV-23182: Server crashes in
# Item::fix_fields_if_needed / table_value_constr::prepare upon 2nd execution of PS
#
SET @save_in_predicate_conversion_threshold=@@in_predicate_conversion_threshold;
SET in_predicate_conversion_threshold=2;
CREATE TABLE t1 (c VARCHAR(10)) DEFAULT CHARSET=utf8;
PREPARE stmt FROM "SELECT * FROM t1 WHERE c IN ('10','20')";
EXECUTE stmt;
c
# Without the patch second execution of the prepared statement 'stmt'
# results in crash.
EXECUTE stmt;
c
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
# Check that the query without conversion doesn't crash server
CREATE TABLE t1 (c VARCHAR(10));
PREPARE stmt FROM "SELECT * FROM t1 WHERE c IN ('10','20')";
EXECUTE stmt;
c
EXECUTE stmt;
c
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
# Test case for a row expression in the left part of the IN clause
CREATE TABLE t1 (a VARCHAR(3), b VARCHAR(3)) DEFAULT CHARSET=utf8;
PREPARE stmt FROM "SELECT * FROM t1 WHERE (a, b) IN (('10', '10'), ('20', '20'))";
EXECUTE stmt;
a b
EXECUTE stmt;
a b
DROP TABLE t1;
# Check that the query without conversion is handled successfully
CREATE TABLE t1 (a VARCHAR(3), b VARCHAR(3));
PREPARE stmt FROM "SELECT * FROM t1 WHERE (a, b) IN (('10', '10'), ('20', '20'))";
EXECUTE stmt;
a b
EXECUTE stmt;
a b
DROP TABLE t1;
SET @@in_predicate_conversion_threshold = @save_in_predicate_conversion_threshold;
End of 10.3 tests
......@@ -1650,4 +1650,50 @@ select * from (values (3),(7),(1) union values (2),(4) order by 1 limit 2) as dt
drop table t1;
--echo #
--echo # MDEV-23182: Server crashes in
--echo # Item::fix_fields_if_needed / table_value_constr::prepare upon 2nd execution of PS
--echo #
SET @save_in_predicate_conversion_threshold=@@in_predicate_conversion_threshold;
SET in_predicate_conversion_threshold=2;
CREATE TABLE t1 (c VARCHAR(10)) DEFAULT CHARSET=utf8;
PREPARE stmt FROM "SELECT * FROM t1 WHERE c IN ('10','20')";
EXECUTE stmt;
--echo # Without the patch second execution of the prepared statement 'stmt'
--echo # results in crash.
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
--echo # Check that the query without conversion doesn't crash server
CREATE TABLE t1 (c VARCHAR(10));
PREPARE stmt FROM "SELECT * FROM t1 WHERE c IN ('10','20')";
EXECUTE stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
--echo # Test case for a row expression in the left part of the IN clause
CREATE TABLE t1 (a VARCHAR(3), b VARCHAR(3)) DEFAULT CHARSET=utf8;
PREPARE stmt FROM "SELECT * FROM t1 WHERE (a, b) IN (('10', '10'), ('20', '20'))";
EXECUTE stmt;
EXECUTE stmt;
DROP TABLE t1;
--echo # Check that the query without conversion is handled successfully
CREATE TABLE t1 (a VARCHAR(3), b VARCHAR(3));
PREPARE stmt FROM "SELECT * FROM t1 WHERE (a, b) IN (('10', '10'), ('20', '20'))";
EXECUTE stmt;
EXECUTE stmt;
DROP TABLE t1;
SET @@in_predicate_conversion_threshold = @save_in_predicate_conversion_threshold;
--echo End of 10.3 tests
......@@ -539,7 +539,10 @@ bool Item_func_in::create_value_list_for_tvc(THD *thd,
if (is_list_of_rows)
{
Item_row *row_list= (Item_row *)(args[i]);
Item_row *row_list= (Item_row *)(args[i]->build_clone(thd));
if (!row_list)
return true;
for (uint j=0; j < row_list->cols(); j++)
{
......@@ -561,7 +564,8 @@ bool Item_func_in::create_value_list_for_tvc(THD *thd,
sprintf(col_name, "_col_%i", 1);
args[i]->set_name(thd, col_name, strlen(col_name), thd->charset());
}
if (tvc_value->push_back(args[i]->real_item()))
Item *arg_clone= args[i]->build_clone(thd);
if (!arg_clone || tvc_value->push_back(arg_clone))
return true;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment