#At file:///home/lsoares/Workspace/bzr/work/bugfixing/54197/mysql-5.1-bugteam/ based on revid:georgi.kodinov@stripped
3423 Luis Soares 2010-06-21
BUG#54197: 'BINLOG ' statements crash the server
Some BINLOG statements would make the server hit an assertion,
because they would lead to setting thd->main_da.m_status
twice. This was caused because the execution of some BINLOG
statements (in base 64 output) make my_ok to be called before
returning from ev->apply_event. Then, my_ok would be called
again, right before returning from mysql_client_binlog_statement,
triggering the assertion.
We fix this by:
1. moving the call to my_ok from inside
mysql_client_binlog_statement function, to
mysql_execute_command
2. changing the signature of mysql_client_binlog_statement so
that it returns non-zero on error and zero otherwise.
3. calling my_ok in mysql_execute_command conditionally, ie, if
the status hasn't been set yet.
modified:
mysql-test/r/mysqlbinlog_base64.result
mysql-test/t/mysqlbinlog_base64.test
sql/mysql_priv.h
sql/sql_binlog.cc
sql/sql_parse.cc
=== modified file 'mysql-test/r/mysqlbinlog_base64.result'
--- a/mysql-test/r/mysqlbinlog_base64.result 2008-07-28 07:15:20 +0000
+++ b/mysql-test/r/mysqlbinlog_base64.result 2010-06-21 00:05:45 +0000
@@ -109,3 +109,9 @@ count(*)
35840
drop table t1;
drop table t2;
+RESET MASTER;
+CREATE TABLE t1(c1 INT) ENGINE=myisam;
+FLUSH LOGS;
+INSERT INTO t1 VALUES(1),(2);
+DROP TABLE t1;
+DROP TABLE t1;
=== modified file 'mysql-test/t/mysqlbinlog_base64.test'
--- a/mysql-test/t/mysqlbinlog_base64.test 2008-07-28 07:15:20 +0000
+++ b/mysql-test/t/mysqlbinlog_base64.test 2010-06-21 00:05:45 +0000
@@ -71,3 +71,23 @@ select count(*) from t2;
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_base64.sql
drop table t1;
drop table t2;
+
+#
+# BUG#54197 'BINLOG ' statements crash the server
+#
+
+RESET MASTER;
+
+CREATE TABLE t1(c1 INT) ENGINE=myisam;
+FLUSH LOGS;
+INSERT INTO t1 VALUES(1),(2);
+
+-- let $fname_prefix= `SELECT UUID()`;
+-- let $outfile= $MYSQL_TEST_DIR/var/tmp/$fname_prefix.sql
+
+--let $MYSQLD_DATADIR=`select @@datadir`
+--exec $MYSQL_BINLOG --base64-output $MYSQLD_DATADIR/master-bin.000001 > $outfile
+DROP TABLE t1;
+--exec $MYSQL test < $outfile
+-- remove_file $outfile
+DROP TABLE t1;
=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h 2010-05-27 20:07:40 +0000
+++ b/sql/mysql_priv.h 2010-06-21 00:05:45 +0000
@@ -1045,7 +1045,7 @@ bool mysql_alter_db(THD *thd, const char
bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db);
void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
-void mysql_client_binlog_statement(THD *thd);
+int mysql_client_binlog_statement(THD *thd);
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
my_bool drop_temporary);
int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
=== modified file 'sql/sql_binlog.cc'
--- a/sql/sql_binlog.cc 2009-10-14 01:39:05 +0000
+++ b/sql/sql_binlog.cc 2010-06-21 00:05:45 +0000
@@ -31,7 +31,7 @@
statement.
*/
-void mysql_client_binlog_statement(THD* thd)
+int mysql_client_binlog_statement(THD* thd)
{
DBUG_ENTER("mysql_client_binlog_statement");
DBUG_PRINT("info",("binlog base64: '%*s'",
@@ -40,7 +40,7 @@ void mysql_client_binlog_statement(THD*
thd->lex->comment.str));
if (check_global_access(thd, SUPER_ACL))
- DBUG_VOID_RETURN;
+ DBUG_RETURN(1);
size_t coded_len= thd->lex->comment.length + 1;
size_t decoded_len= base64_needed_decoded_length(coded_len);
@@ -56,7 +56,7 @@ void mysql_client_binlog_statement(THD*
Format_description_event.
*/
my_bool have_fd_event= TRUE;
- int err;
+ int err= 0;
Relay_log_info *rli;
rli= thd->rli_fake;
if (!rli)
@@ -85,6 +85,7 @@ void mysql_client_binlog_statement(THD*
rli->relay_log.description_event_for_exec &&
buf))
{
+ err= 1;
my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */
goto end;
}
@@ -111,6 +112,7 @@ void mysql_client_binlog_statement(THD*
if (bytes_decoded < 0)
{
+ err= 1;
my_error(ER_BASE64_DECODE_ERROR, MYF(0));
goto end;
}
@@ -150,6 +152,7 @@ void mysql_client_binlog_statement(THD*
event_len, bytes_decoded));
if (bytes_decoded < EVENT_LEN_OFFSET || (uint) bytes_decoded < event_len)
{
+ err= 1;
my_error(ER_SYNTAX_ERROR, MYF(0));
goto end;
}
@@ -166,6 +169,7 @@ void mysql_client_binlog_statement(THD*
have_fd_event= TRUE;
else
{
+ err= 1;
my_error(ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT,
MYF(0), Log_event::get_type_str((Log_event_type)type));
goto end;
@@ -183,6 +187,7 @@ void mysql_client_binlog_statement(THD*
causes by a bad statement
*/
my_error(ER_SYNTAX_ERROR, MYF(0));
+ err= 1;
goto end;
}
@@ -238,10 +243,9 @@ void mysql_client_binlog_statement(THD*
DBUG_PRINT("info",("binlog base64 execution finished successfully"));
- my_ok(thd);
end:
rli->clear_tables_to_lock();
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(err);
}
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2010-06-07 10:01:54 +0000
+++ b/sql/sql_parse.cc 2010-06-21 00:05:45 +0000
@@ -4922,7 +4922,15 @@ create_sp_error:
case SQLCOM_BINLOG_BASE64_EVENT:
{
#ifndef EMBEDDED_LIBRARY
- mysql_client_binlog_statement(thd);
+ if (!mysql_client_binlog_statement(thd) && !thd->main_da.is_set())
+ /*
+ Applying some 'BINLOG' statements makes my_ok to be called
+ right after applying the event (ev->apply_event). So we
+ need to check if the thd status hasn't yet been set, before
+ we set it here, thence not risking setting OK twice.
+ See BUG#54197.
+ */
+ my_ok(thd);
#else /* EMBEDDED_LIBRARY */
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "embedded");
#endif /* EMBEDDED_LIBRARY */
Attachment: [text/bzr-bundle] bzr/luis.soares@sun.com-20100621000545-b0vqlex072xeeq55.bundle