From 00b2fc6affe6ffcef7f50c77b1fb2cbc3116560b Mon Sep 17 00:00:00 2001 From: "kroki/tomash@moonlight.intranet" <> Date: Thu, 19 Oct 2006 14:43:52 +0400 Subject: [PATCH] BUG#21856: Prepared Statements: crash if bad create When statement to be prepared contained CREATE PROCEDURE, CREATE FUNCTION or CREATE TRIGGER statements with a syntax error in it, the preparation would fail with syntax error message, but the memory could be corrupted. The problem occurred because we switch memroot when parse stored routine or trigger definitions, and on parse error we restored the original memroot only after performing some memory operations. In more detail: - prepared statement would activate its own memory root to parse the definition of the stored procedure. - SP would reset this memory root with its own memory root to parse SP statements - a syntax error would happen - prepared statement would restore the original memory root - stored procedure would restore what it thinks was the original memory root, but actually was the statement memory root. That led to double free - in destruction of the statement and in a next call to mysql_parse(). The solution is to restore memroot right after the failed parsing. --- mysql-test/r/ps.result | 1 + mysql-test/t/ps.test | 20 ++++++++++++++++++++ sql/sql_parse.cc | 9 +++++++-- sql/sql_prepare.cc | 11 +++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 080187cfa7b..c895ef54e55 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1311,4 +1311,5 @@ EXECUTE stmt USING @a; i j i i j DEALLOCATE PREPARE stmt; DROP TABLE IF EXISTS t1, t2, t3; +DROP PROCEDURE IF EXISTS p1; End of 5.0 tests. diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 5b2e37ecc94..fd74c52f3d5 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1358,4 +1358,24 @@ DEALLOCATE PREPARE stmt; DROP TABLE IF EXISTS t1, t2, t3; +# +# BUG#21856: Prepared Statments: crash if bad create +# +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +--enable_warnings + +let $iterations= 100; +--disable_query_log +--disable_result_log +while ($iterations > 0) +{ + --error ER_PARSE_ERROR + PREPARE stmt FROM "CREATE PROCEDURE p1()"; + dec $iterations; +} +--enable_query_log +--enable_result_log + + --echo End of 5.0 tests. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2acbf18f1e6..f75bc5f073c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5852,14 +5852,19 @@ void mysql_parse(THD *thd, char *inBuf, uint length) DBUG_ASSERT(thd->net.report_error); DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); - query_cache_abort(&thd->net); - lex->unit.cleanup(); + + /* + The first thing we do after parse error is freeing sp_head to + ensure that we have restored original memroot. + */ if (thd->lex->sphead) { /* Clean up after failed stored procedure/function */ delete thd->lex->sphead; thd->lex->sphead= NULL; } + query_cache_abort(&thd->net); + lex->unit.cleanup(); } thd->proc_info="freeing items"; thd->end_statement(); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 32f0ca6859d..fcbf495463a 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2773,6 +2773,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) error= MYSQLparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); + + /* + The first thing we do after parse error is freeing sp_head to + ensure that we have restored original memroot. + */ + if (error && lex->sphead) + { + delete lex->sphead; + lex->sphead= NULL; + } /* While doing context analysis of the query (in check_prepared_statement) we allocate a lot of additional memory: for open tables, JOINs, derived @@ -2798,6 +2808,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) if (error == 0) error= check_prepared_statement(this, name.str != 0); + /* Free sp_head if check_prepared_statement() failed. */ if (error && lex->sphead) { delete lex->sphead;