diff options
author | Dmitry Shulga <dmitry.shulga@mariadb.com> | 2022-04-11 13:37:55 +0700 |
---|---|---|
committer | Dmitry Shulga <dmitry.shulga@mariadb.com> | 2022-04-11 13:37:55 +0700 |
commit | 1d52bf0ac68133a1292a131bee49fcf4e3132540 (patch) | |
tree | 58a3eef8482830906c278d90680639927de111dd | |
parent | 27b5d814e2c851569a0d75e4217da4a552b79f25 (diff) | |
download | mariadb-git-bb-10.2-MDEV-16128.tar.gz |
MDEV-16128: Server crash in Item_func::print_op on 2nd execution of PSbb-10.2-MDEV-16128
For some queries that involve tables with different but convertible
character sets for columns taking part in the query, repeatable
execution of such queries in PS mode or as part of a stored routine
would result in server abnormal termination.
For example,
CREATE TABLE t1 (a2 varchar(10));
CREATE TABLE t2 (u1 varchar(10) CHARACTER SET utf8);
CREATE TABLE t3 (u2 varchar(10) CHARACTER SET utf8);
PREPARE stmt FROM
"SELECT t1.* FROM (t1 JOIN t2 ON (t2.u1 = t1.a2))
WHERE (EXISTS (SELECT 1 FROM t3 WHERE t3.u2 = t1.a2))";
EXECUTE stmt;
EXECUTE stmt; <== Running this prepared statement the second time
results in server crash.
The reason of server crash is that an instance of the class
Item_func_conv_charset, that created for conversion of a column
from one character set to another, is allocated on execution
memory root but pointer to this instance is stored in an item
placed on prepared statement memory root. Below is calls trace to
the place where an instance of the class Item_func_conv_charset
is created.
setup_conds
Item_func::fix_fields
Item_bool_rowready_func2::fix_length_and_dec
Item_func::setup_args_and_comparator
Item_func_or_sum::agg_arg_charsets_for_comparison
Item_func_or_sum::agg_arg_charsets
Item_func_or_sum::agg_item_set_converter
Item::safe_charset_converter
And the following trace shows the point where a pointer to
the instance of Item_func_conv_charset is passed to the class
Item_func_eq, that is created on a memory root of the prepared
statement.
Prepared_statement::execute
mysql_execute_command
execute_sqlcom_select
handle_select
mysql_select
JOIN::optimize
JOIN::optimize_inner
convert_join_subqueries_to_semijoins
convert_subq_to_sj
---
Item_func_eq *item_eq=
new (thd->mem_root) Item_func_eq(thd, subq_pred->left_expr_orig,
subq_lex->ref_pointer_array[0]);
On completing execution of the statement EXECUTE stmt
the items placed on execution query arena are released but the instance
of Item_func_eq that was created on permanent memory root still does exist
and has one of its arguments (stored in args[0]) referencing to the released
object. Next time the prepared statement is executed access to dangling
pointer leads to server crash.
So, to fix the issue the item Item_func_conv_charset should be created
on permanent memory root in case a prepared statement or stored routine
is run.
-rw-r--r-- | mysql-test/r/ps.result | 16 | ||||
-rw-r--r-- | mysql-test/t/ps.test | 16 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 14 |
3 files changed, 45 insertions, 1 deletions
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 81167bc85c0..77bfcb9eabb 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -5509,5 +5509,21 @@ id select_type table type possible_keys key key_len ref rows Extra NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL DEALLOCATE PREPARE stmt; DROP TABLE t1; +# +# MDEV-16128: Server crash in Item_func::print_op on 2nd execution of PS +# +CREATE TABLE t1 (a2 varchar(10)); +CREATE TABLE t2 (u1 varchar(10) CHARACTER SET utf8 ); +CREATE TABLE t3 (u2 varchar(10) CHARACTER SET utf8); +PREPARE stmt FROM "SELECT t1.* FROM (t1 JOIN t2 ON (t2.u1 = t1.a2)) WHERE (EXISTS (SELECT 1 FROM t3 WHERE t3.u2 = t1.a2))"; +EXECUTE stmt; +a2 +# Without the patch second execution of the prepared statement +# would lead to server crash. +EXECUTE stmt; +a2 +# Clean up +DEALLOCATE PREPARE stmt; +DROP TABLE t1, t2, t3; # End of 10.2 tests # diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 81d8e8e0fed..233ef8c369c 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -4990,5 +4990,21 @@ EXECUTE stmt; DEALLOCATE PREPARE stmt; DROP TABLE t1; +--echo # +--echo # MDEV-16128: Server crash in Item_func::print_op on 2nd execution of PS +--echo # + +CREATE TABLE t1 (a2 varchar(10)); +CREATE TABLE t2 (u1 varchar(10) CHARACTER SET utf8 ); +CREATE TABLE t3 (u2 varchar(10) CHARACTER SET utf8); +PREPARE stmt FROM "SELECT t1.* FROM (t1 JOIN t2 ON (t2.u1 = t1.a2)) WHERE (EXISTS (SELECT 1 FROM t3 WHERE t3.u2 = t1.a2))"; +EXECUTE stmt; +--echo # Without the patch second execution of the prepared statement +--echo # would lead to server crash. +EXECUTE stmt; +--echo # Clean up +DEALLOCATE PREPARE stmt; +DROP TABLE t1, t2, t3; + --echo # End of 10.2 tests --echo # diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3a76982d80e..19bd6176bd1 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -513,19 +513,31 @@ bool Item_func::setup_args_and_comparator(THD *thd, Arg_comparator *cmp) { DBUG_ASSERT(arg_count >= 2); // Item_func_nullif has arg_count == 3 + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); + if (args[0]->cmp_type() == STRING_RESULT && args[1]->cmp_type() == STRING_RESULT) { DTCollation tmp; if (agg_arg_charsets_for_comparison(tmp, args, 2)) + { + if (arena) + thd->restore_active_arena(arena, &backup); + return true; + } cmp->m_compare_collation= tmp.collation; } // Convert constants when compared to int/year field DBUG_ASSERT(functype() != LIKE_FUNC); convert_const_compared_to_int_field(thd); - return cmp->set_cmp_func(this, &args[0], &args[1], true); + bool ret= cmp->set_cmp_func(this, &args[0], &args[1], true); + if (arena) + thd->restore_active_arena(arena, &backup); + + return ret; } |