Skip to content

Commit

Permalink
MDEV-23182: Server crashes in Item::fix_fields_if_needed / table_valu…
Browse files Browse the repository at this point in the history
…e_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.
  • Loading branch information
dmitryshulga committed Dec 16, 2021
1 parent 7bc629a commit a65d01a
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 2 deletions.
42 changes: 42 additions & 0 deletions mysql-test/main/table_value_constr.result
Original file line number Diff line number Diff line change
Expand Up @@ -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
46 changes: 46 additions & 0 deletions mysql-test/main/table_value_constr.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 6 additions & 2 deletions sql/sql_tvc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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++)
{
Expand All @@ -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;
}

Expand Down

0 comments on commit a65d01a

Please sign in to comment.