From b74795b00cf36b5d8d733e0056a64f2490fd51ea Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Wed, 29 Jul 2015 21:38:45 +0300 Subject: [PATCH] MDEV-7040: Crash in field_conv, memcpy_field_possible, part#2 The problem was with Materialized_cursor and temporary table it uses. Temorary table's fields had Field::orig_table pointing to the tables that were used in the query that produced data for the cursor. When "FETCH INTO sp_var" statement is executed, those original tables were already closed. However, copying from Materialized_cursor's table into SP variable may cause field_conv() to be invoked which calls field->type() which may access field->orig_table (for certain field types). Fixed by setting Materialized_cursor->table->field[i]->orig_table to point to Materialized_cursor->table. (this is how it is done for regular base tables) --- mysql-test/r/sp.result | 53 ++++++++++++++++++++++++++++++++ mysql-test/t/sp.test | 68 ++++++++++++++++++++++++++++++++++++++++++ sql/sql_cursor.cc | 37 +++++++++++++++++++++++ 3 files changed, 158 insertions(+) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 56d8ea62f44..cf50bf49004 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -7858,3 +7858,56 @@ v1 DROP PROCEDURE p1; DROP TABLE t1; # End of 5.5 test +# +# MDEV-7040: Crash in field_conv, memcpy_field_possible, part#2 +# +create table t1 ( +col1 bigint(20), +col2 char(1), +col3 char(2) +); +insert into t1 values (1,'a','a'), (2,'b','b'); +create table t2 as select * from t1; +create table t3 as select * from t1; +create table t4 as select * from t1; +create table t5 as select * from t1; +create table t6 as select * from t1; +flush tables; +CREATE PROCEDURE p1() +begin +DECLARE _var1 bigint(20) UNSIGNED; +DECLARE _var2 CHAR(1) DEFAULT NULL; +DECLARE _var3 CHAR(1) DEFAULT NULL; +DECLARE _done BOOLEAN DEFAULT 0; +declare cur1 cursor for +select col1, col2, col3 +from t1 +where +col1 in (select t2.col1 from t2 where t2.col2=t1.col2) or +col2 in (select t3.col3 from t3 where t3.col3=t1.col2) ; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET _done = 1; +OPEN cur1; +set _var1 = (select _var1 from t4 limit 1); +set _var1 = (select _var1 from t5 limit 1); +set _var1 = (select _var1 from t6 limit 1); +label1: +LOOP +SET _done = 0; +FETCH cur1 INTO _var1, _var2, _var3; +IF _done THEN +LEAVE label1; +END IF; +END LOOP label1; +CLOSE cur1; +end| +set @tmp_toc= @@table_open_cache; +set @tmp_tdc= @@table_definition_cache; +set global table_open_cache=1; +set global table_definition_cache=1; +Warnings: +Warning 1292 Truncated incorrect table_definition_cache value: '1' +call p1(); +set global table_open_cache= @tmp_toc; +set global table_definition_cache= @tmp_tdc; +drop procedure p1; +drop table t1,t2,t3,t4,t5,t6; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index d378ab551e6..c62257049ee 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -9303,3 +9303,71 @@ DROP PROCEDURE p1; DROP TABLE t1; --echo # End of 5.5 test + +--echo # +--echo # MDEV-7040: Crash in field_conv, memcpy_field_possible, part#2 +--echo # +create table t1 ( + col1 bigint(20), + col2 char(1), + col3 char(2) +); +insert into t1 values (1,'a','a'), (2,'b','b'); + +create table t2 as select * from t1; +create table t3 as select * from t1; +create table t4 as select * from t1; +create table t5 as select * from t1; +create table t6 as select * from t1; + +flush tables; + +DELIMITER |; + +CREATE PROCEDURE p1() +begin + DECLARE _var1 bigint(20) UNSIGNED; + DECLARE _var2 CHAR(1) DEFAULT NULL; + DECLARE _var3 CHAR(1) DEFAULT NULL; + + DECLARE _done BOOLEAN DEFAULT 0; + + declare cur1 cursor for + select col1, col2, col3 + from t1 + where + col1 in (select t2.col1 from t2 where t2.col2=t1.col2) or + col2 in (select t3.col3 from t3 where t3.col3=t1.col2) ; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET _done = 1; + + OPEN cur1; + + set _var1 = (select _var1 from t4 limit 1); + set _var1 = (select _var1 from t5 limit 1); + set _var1 = (select _var1 from t6 limit 1); +label1: + LOOP + SET _done = 0; + FETCH cur1 INTO _var1, _var2, _var3; + IF _done THEN + LEAVE label1; + END IF; + END LOOP label1; + CLOSE cur1; +end| +DELIMITER ;| + +set @tmp_toc= @@table_open_cache; +set @tmp_tdc= @@table_definition_cache; + +set global table_open_cache=1; +set global table_definition_cache=1; +call p1(); + +set global table_open_cache= @tmp_toc; +set global table_definition_cache= @tmp_tdc; +drop procedure p1; + +drop table t1,t2,t3,t4,t5,t6; + diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index c09f3269d7a..d8684086e68 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -54,6 +54,8 @@ public: virtual void fetch(ulong num_rows); virtual void close(); virtual ~Materialized_cursor(); + + void on_table_fill_finished(); }; @@ -74,6 +76,18 @@ public: Select_materialize(select_result *result_arg) :result(result_arg), materialized_cursor(0) {} virtual bool send_result_set_metadata(List &list, uint flags); + bool send_eof() + { + if (materialized_cursor) + materialized_cursor->on_table_fill_finished(); + return false; + } + + void abort_result_set() + { + if (materialized_cursor) + materialized_cursor->on_table_fill_finished(); + } }; @@ -388,6 +402,29 @@ Materialized_cursor::~Materialized_cursor() } +/* + @brief + Perform actions that are to be done when cursor materialization has + finished. + + @detail + This function is called when "OPEN $cursor" has finished filling the + temporary table with rows that the cursor will return. + + Temporary table has table->field->orig_table pointing at the tables + that are used in the cursor definition query. Pointers to these tables + will not be valid after the query finishes. So, we do what is done for + regular tables: have orig_table point at the table that the fields belong + to. +*/ + +void Materialized_cursor::on_table_fill_finished() +{ + uint fields= table->s->fields; + for (uint i= 0; i < fields; i++) + table->field[i]->orig_table= table->field[i]->table; +} + /*************************************************************************** Select_materialize ****************************************************************************/