From: Nuno Carvalho Date: March 26 2012 9:30pm Subject: bzr push into mysql-trunk branch (nuno.carvalho:3860 to 3861) Bug#13799555 List-Archive: http://lists.mysql.com/commits/143317 X-Bug: 13799555 Message-Id: <201203262131.q2QLV0Mw030372@acsmt357.oracle.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 3861 Nuno Carvalho 2012-03-26 Bug#13799555: ROWS_QUERY_LOG_EVENTS DOES NOT ESCAPE MULTI-LINE QUERIES PROPERLY When binlog_rows_query_log_events = 1 and a statement is written to the binary log in row format, the server generates a log event containing the original query text. If mysqlbinlog is given the option --verbose --verbose the original statement is printed. To prevent the statement from being executed, it is prefixed by '#'. However, this is not enough for multi-line statements: only the first line of the query will be commented out. If a malicious user knows that the binary log will be processed using mysqlbinlog --verbose --verbose, then the user can execute arbitrary statements on the server. Prefix every line of a multi-line query with '#' to prevent the statement from being executed when binary log will be processed using 'mysqlbinlog --verbose --verbose'. modified: mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test sql/log_event.cc 3860 Martin Zaun 2012-03-26 Bug#54854 - updated copyright headers modified: sql/rpl_slave.cc sql/rpl_slave.h === modified file 'mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result' --- a/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result 2009-10-14 13:25:11 +0000 +++ b/mysql-test/suite/binlog/r/binlog_row_mysqlbinlog_verbose.result 2012-03-26 21:29:37 +0000 @@ -159,3 +159,9 @@ stmt ### WHERE ### @1=2 drop table raw_binlog_rows; +SET @@SESSION.BINLOG_ROWS_QUERY_LOG_EVENTS = 1; +CREATE TABLE t1 (a VARCHAR(50)); +INSERT INTO t1 VALUES (" + GRANT ALL ON *.* TO 'batman'/*!*/; + ")| +DROP TABLE t1; === modified file 'mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test' --- a/mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test 2009-10-14 13:25:11 +0000 +++ b/mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_verbose.test 2012-03-26 21:29:37 +0000 @@ -32,9 +32,6 @@ # tool and the output is checked. # ######################################################## - -# We require binlog_format_row as we're independent of binlog format -# and there's no point running the same test 3 times -- source include/have_binlog_format_row.inc --disable_query_log @@ -84,3 +81,63 @@ create table raw_binlog_rows (txt varcha # Output --verbose lines, with extra Windows CR's trimmed select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%'; drop table raw_binlog_rows; + + +####################################################################### +# BUG#13799555: ROWS_QUERY_LOG_EVENTS DOES NOT ESCAPE MULTI-LINE QUERIES PROPERLY +# +# Check that when mysqlbinlog is given the option --verbose --verbose, +# the multi-line original statement are properly escaped to prevent from +# being executed. +SET @@SESSION.BINLOG_ROWS_QUERY_LOG_EVENTS = 1; +CREATE TABLE t1 (a VARCHAR(50)); +--let $binlog_start_position= query_get_value("SHOW MASTER STATUS", Position, 1) + +--delimiter | +INSERT INTO t1 VALUES (" + GRANT ALL ON *.* TO 'batman'/*!*/; + ")| +--delimiter ; + +--let $binlog_stop_position= query_get_value("SHOW MASTER STATUS", Position, 1) +--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) +--let $datadir= `SELECT @@datadir` + +--let $_prefix= `SELECT UUID()` +--let $TMP_FILE= $MYSQLTEST_VARDIR/tmp/$_prefix.tmp +--exec $MYSQL_BINLOG --force-if-open --verbose --verbose --start-position=$binlog_start_position --stop-position=$binlog_stop_position $datadir/$binlog_file > $TMP_FILE + +--let TMP_FILE= $TMP_FILE +--perl END_OF_FILE +my $tmp_file= $ENV{'TMP_FILE'}; + +my $escaped_query = < }; +close(FILE) or die("Unable to close file."); + +$match= index($contents, $escaped_query) > -1; +if (!$match) +{ + print "\n====================================================\n"; + print "ESCAPED STRING DID NOT MATCH:\n"; + print "====================================================\n"; + print "$escaped_query"; + print "====================================================\n"; + + print "\n====================================================\n"; + print "BINLOG CONTENTS\n"; + print "====================================================\n"; + print "$contents"; + print "====================================================\n"; +} +END_OF_FILE + +# Clean up +DROP TABLE t1; +--remove_file $TMP_FILE === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2012-03-22 17:33:57 +0000 +++ b/sql/log_event.cc 2012-03-26 21:29:37 +0000 @@ -11807,9 +11807,21 @@ Rows_query_log_event::print(FILE *file, { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; + uint m_rows_query_len= strlen(m_rows_query); + char rows_query_copy[m_rows_query_len + 1]; + char *token= NULL, *saveptr= NULL; + print_header(head, print_event_info, FALSE); my_b_printf(head, "\tRows_query\n"); - my_b_printf(head, "# %s\n", m_rows_query); + /* + Prefix every line of a multi-line query with '#' to prevent the + statement from being executed when binary log will be processed + using 'mysqlbinlog --verbose --verbose'. + */ + strmake(rows_query_copy, m_rows_query, m_rows_query_len); + for (token= strtok_r(rows_query_copy, "\n", &saveptr); token; + token= strtok_r(NULL, "\n", &saveptr)) + my_b_printf(head, "# %s\n", token); print_base64(body, print_event_info, true); } } No bundle (reason: useless for push emails).