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.
parent 0f057bfc
...@@ -1311,4 +1311,5 @@ EXECUTE stmt USING @a; ...@@ -1311,4 +1311,5 @@ EXECUTE stmt USING @a;
i j i i j i j i i j
DEALLOCATE PREPARE stmt; DEALLOCATE PREPARE stmt;
DROP TABLE IF EXISTS t1, t2, t3; DROP TABLE IF EXISTS t1, t2, t3;
DROP PROCEDURE IF EXISTS p1;
End of 5.0 tests. End of 5.0 tests.
...@@ -1358,4 +1358,24 @@ DEALLOCATE PREPARE stmt; ...@@ -1358,4 +1358,24 @@ DEALLOCATE PREPARE stmt;
DROP TABLE IF EXISTS t1, t2, t3; 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. --echo End of 5.0 tests.
...@@ -5852,14 +5852,19 @@ void mysql_parse(THD *thd, char *inBuf, uint length) ...@@ -5852,14 +5852,19 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
DBUG_ASSERT(thd->net.report_error); DBUG_ASSERT(thd->net.report_error);
DBUG_PRINT("info",("Command aborted. Fatal_error: %d", DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error)); 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) if (thd->lex->sphead)
{ {
/* Clean up after failed stored procedure/function */ /* Clean up after failed stored procedure/function */
delete thd->lex->sphead; delete thd->lex->sphead;
thd->lex->sphead= NULL; thd->lex->sphead= NULL;
} }
query_cache_abort(&thd->net);
lex->unit.cleanup();
} }
thd->proc_info="freeing items"; thd->proc_info="freeing items";
thd->end_statement(); thd->end_statement();
......
...@@ -2773,6 +2773,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) ...@@ -2773,6 +2773,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
error= MYSQLparse((void *)thd) || thd->is_fatal_error || error= MYSQLparse((void *)thd) || thd->is_fatal_error ||
thd->net.report_error || init_param_array(this); 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) While doing context analysis of the query (in check_prepared_statement)
we allocate a lot of additional memory: for open tables, JOINs, derived 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) ...@@ -2798,6 +2808,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
if (error == 0) if (error == 0)
error= check_prepared_statement(this, name.str != 0); error= check_prepared_statement(this, name.str != 0);
/* Free sp_head if check_prepared_statement() failed. */
if (error && lex->sphead) if (error && lex->sphead)
{ {
delete lex->sphead; delete lex->sphead;
......
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