Bug#42634: % character in query can cause mysqld signal 11 segfault

The problem is that a unfiltered user query was being passed as
the format string parameter of sql_print_warning which later
performs printf-like formatting, leading to crashes if the user
query contains formatting instructions (ie: %s). Also, it was
using THD::query as the source of the user query, but this
variable is not meaningful in some situations -- in a delayed
insert, it points to the table name.

The solution is to pass the user query as a parameter for the
format string and use the function parameter query_arg as the
source of the user query.

mysql-test/suite/binlog/r/binlog_unsafe.result:
  Add test case result for Bug#42634
mysql-test/suite/binlog/t/binlog_unsafe.test:
  Add test case for Bug#42634
sql/sql_class.cc:
  Don't pass the user query as a format string.
This commit is contained in:
Davi Arnaut 2009-02-09 16:17:58 -02:00
parent 461cad77b4
commit bab4ff1ae5
3 changed files with 24 additions and 5 deletions

View File

@ -220,3 +220,10 @@ Warning 1592 Statement is not safe to log in statement format.
Warning 1592 Statement is not safe to log in statement format. Warning 1592 Statement is not safe to log in statement format.
DROP PROCEDURE p1; DROP PROCEDURE p1;
DROP TABLE t1; DROP TABLE t1;
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (a VARCHAR(100), b VARCHAR(100));
INSERT INTO t1 VALUES ('a','b');
UPDATE t1 SET b = '%s%s%s%s%s%s%s%s%s%s%s%s%s%s' WHERE a = 'a' LIMIT 1;
Warnings:
Warning 1592 Statement is not safe to log in statement format.
DROP TABLE t1;

View File

@ -257,3 +257,17 @@ delimiter ;|
CALL p1(); CALL p1();
DROP PROCEDURE p1; DROP PROCEDURE p1;
DROP TABLE t1; DROP TABLE t1;
#
# Bug#42634: % character in query can cause mysqld signal 11 segfault
#
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
CREATE TABLE t1 (a VARCHAR(100), b VARCHAR(100));
INSERT INTO t1 VALUES ('a','b');
UPDATE t1 SET b = '%s%s%s%s%s%s%s%s%s%s%s%s%s%s' WHERE a = 'a' LIMIT 1;
DROP TABLE t1;

View File

@ -3660,16 +3660,14 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
if (lex->is_stmt_unsafe() && if (lex->is_stmt_unsafe() &&
variables.binlog_format == BINLOG_FORMAT_STMT) variables.binlog_format == BINLOG_FORMAT_STMT)
{ {
DBUG_ASSERT(this->query != NULL);
push_warning(this, MYSQL_ERROR::WARN_LEVEL_WARN, push_warning(this, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_BINLOG_UNSAFE_STATEMENT, ER_BINLOG_UNSAFE_STATEMENT,
ER(ER_BINLOG_UNSAFE_STATEMENT)); ER(ER_BINLOG_UNSAFE_STATEMENT));
if (!(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED)) if (!(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED))
{ {
char warn_buf[MYSQL_ERRMSG_SIZE]; sql_print_warning("%s Statement: %.*s",
my_snprintf(warn_buf, MYSQL_ERRMSG_SIZE, "%s Statement: %s", ER(ER_BINLOG_UNSAFE_STATEMENT),
ER(ER_BINLOG_UNSAFE_STATEMENT), this->query); MYSQL_ERRMSG_SIZE, query_arg);
sql_print_warning(warn_buf);
binlog_flags|= BINLOG_FLAG_UNSAFE_STMT_PRINTED; binlog_flags|= BINLOG_FLAG_UNSAFE_STMT_PRINTED;
} }
} }