#At file:///Users/shulga/projects/mysql/mysql-5.5/ based on revid:marc.alff@stripped
3362 Dmitry Shulga 2011-03-11 [merge]
Manual merge from mysql-5.1 for Bug#11764168 (56976: Severe denial
of service in prepared statements).
modified:
mysql-test/r/mysqld--help-notwin.result
mysql-test/r/mysqld--help-win.result
mysql-test/r/variables.result
mysql-test/t/variables.test
sql/item.cc
sql/mysqld.cc
sql/mysqld.h
sql/sql_prepare.cc
sql/sys_vars.cc
tests/mysql_client_test.c
=== modified file 'mysql-test/r/mysqld--help-notwin.result'
--- a/mysql-test/r/mysqld--help-notwin.result 2011-01-14 13:21:46 +0000
+++ b/mysql-test/r/mysqld--help-notwin.result 2011-03-11 13:00:21 +0000
@@ -309,6 +309,10 @@ The following options may be given as th
max_join_size records return an error
--max-length-for-sort-data=#
Max number of bytes in sorted records
+ --max-long-data-size=#
+ The maximum BLOB length to send to server from
+ mysql_send_long_data API. Deprecated option; use
+ max_allowed_packet instead.
--max-prepared-stmt-count=#
Maximum number of prepared statements in the server
--max-relay-log-size=#
@@ -830,6 +834,7 @@ max-error-count 64
max-heap-table-size 16777216
max-join-size 18446744073709551615
max-length-for-sort-data 1024
+max-long-data-size 1048576
max-prepared-stmt-count 16382
max-relay-log-size 0
max-seeks-for-key 18446744073709551615
=== modified file 'mysql-test/r/mysqld--help-win.result'
--- a/mysql-test/r/mysqld--help-win.result 2011-01-14 13:21:46 +0000
+++ b/mysql-test/r/mysqld--help-win.result 2011-03-11 13:00:21 +0000
@@ -308,6 +308,10 @@ The following options may be given as th
max_join_size records return an error
--max-length-for-sort-data=#
Max number of bytes in sorted records
+ --max-long-data-size=#
+ The maximum BLOB length to send to server from
+ mysql_send_long_data API. Deprecated option; use
+ max_allowed_packet instead.
--max-prepared-stmt-count=#
Maximum number of prepared statements in the server
--max-relay-log-size=#
@@ -833,6 +837,7 @@ max-error-count 64
max-heap-table-size 16777216
max-join-size 18446744073709551615
max-length-for-sort-data 1024
+max-long-data-size 1048576
max-prepared-stmt-count 16382
max-relay-log-size 0
max-seeks-for-key 18446744073709551615
=== modified file 'mysql-test/r/variables.result'
--- a/mysql-test/r/variables.result 2011-02-10 07:34:22 +0000
+++ b/mysql-test/r/variables.result 2011-03-11 13:00:21 +0000
@@ -1540,6 +1540,9 @@ ERROR HY000: Cannot drop default keycach
SET @@global.key_cache_block_size=0;
Warnings:
Warning 1292 Truncated incorrect key_cache_block_size value: '0'
+select @@max_long_data_size;
+@@max_long_data_size
+1048576
SET @@global.max_binlog_cache_size=DEFAULT;
SET @@global.max_join_size=DEFAULT;
SET @@global.key_buffer_size=@kbs;
=== modified file 'mysql-test/t/variables.test'
--- a/mysql-test/t/variables.test 2011-02-10 07:34:22 +0000
+++ b/mysql-test/t/variables.test 2011-03-11 13:00:21 +0000
@@ -1281,6 +1281,11 @@ SET @@global.max_join_size=0;
SET @@global.key_buffer_size=0;
SET @@global.key_cache_block_size=0;
+#
+# Bug#56976: added new start-up parameter
+#
+select @@max_long_data_size;
+
# cleanup
SET @@global.max_binlog_cache_size=DEFAULT;
SET @@global.max_join_size=DEFAULT;
=== modified file 'sql/item.cc'
--- a/sql/item.cc 2011-03-04 15:43:28 +0000
+++ b/sql/item.cc 2011-03-11 13:00:21 +0000
@@ -2885,6 +2885,17 @@ bool Item_param::set_longdata(const char
(here), and first have to concatenate all pieces together,
write query to the binary log and only then perform conversion.
*/
+
+ if (str_value.length() + length > max_long_data_size)
+ {
+ my_message(ER_UNKNOWN_ERROR,
+ "Parameter of prepared statement which is set through "
+ "mysql_send_long_data() is longer than "
+ "'max_long_data_size' bytes",
+ MYF(0));
+ DBUG_RETURN(true);
+ }
+
if (str_value.append(str, length, &my_charset_bin))
DBUG_RETURN(TRUE);
state= LONG_DATA_VALUE;
=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc 2011-03-10 08:43:55 +0000
+++ b/sql/mysqld.cc 2011-03-11 13:00:21 +0000
@@ -324,6 +324,7 @@ static PSI_rwlock_key key_rwlock_openssl
/* the default log output is log tables */
static bool lower_case_table_names_used= 0;
+static bool max_long_data_size_used= false;
static bool volatile select_thread_in_use, signal_thread_in_use;
/* See Bug#56666 and Bug#56760 */;
volatile bool ready_to_exit;
@@ -478,6 +479,11 @@ ulong specialflag=0;
ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
ulong max_connections, max_connect_errors;
+/*
+ Maximum length of parameter value which can be set through
+ mysql_send_long_data() call.
+*/
+ulong max_long_data_size;
/**
Limit of the total number of prepared statements in the server.
Is necessary to protect the server against out-of-memory attacks.
@@ -7155,6 +7161,10 @@ mysqld_get_one_option(int optid,
if (argument == NULL) /* no argument */
log_error_file_ptr= const_cast<char*>("");
break;
+ case OPT_MAX_LONG_DATA_SIZE:
+ max_long_data_size_used= true;
+ WARN_DEPRECATED(NULL, 5, 6, "--max_long_data_size", "'--max_allowed_packet'");
+ break;
}
return 0;
}
@@ -7381,6 +7391,13 @@ static int get_options(int *argc_ptr, ch
opt_readonly= read_only;
+ /*
+ If max_long_data_size is not specified explicitly use
+ value of max_allowed_packet.
+ */
+ if (!max_long_data_size_used)
+ max_long_data_size= global_system_variables.max_allowed_packet;
+
return 0;
}
=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h 2011-03-10 08:43:55 +0000
+++ b/sql/mysqld.h 2011-03-11 13:00:21 +0000
@@ -126,6 +126,7 @@ extern char *default_storage_engine;
extern bool opt_endinfo, using_udf_functions;
extern my_bool locked_in_memory;
extern bool opt_using_transactions;
+extern ulong max_long_data_size;
extern ulong current_pid;
extern ulong expire_logs_days;
extern my_bool relay_log_recovery;
@@ -397,7 +398,8 @@ enum options_mysqld
OPT_UPDATE_LOG,
OPT_WANT_CORE,
OPT_ENGINE_CONDITION_PUSHDOWN,
- OPT_LOG_ERROR
+ OPT_LOG_ERROR,
+ OPT_MAX_LONG_DATA_SIZE
};
=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc 2011-03-01 14:42:37 +0000
+++ b/sql/sql_prepare.cc 2011-03-11 13:00:21 +0000
@@ -2784,6 +2784,34 @@ void mysql_sql_stmt_close(THD *thd)
}
}
+
+class Set_longdata_error_handler : public Internal_error_handler
+{
+public:
+ Set_longdata_error_handler(Prepared_statement *statement)
+ : stmt(statement)
+ { }
+
+public:
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+ {
+ stmt->state= Query_arena::ERROR;
+ stmt->last_errno= sql_errno;
+ strncpy(stmt->last_error, msg, MYSQL_ERRMSG_SIZE);
+
+ return TRUE;
+ }
+
+private:
+ Prepared_statement *stmt;
+};
+
+
/**
Handle long data in pieces from client.
@@ -2840,16 +2868,19 @@ void mysql_stmt_get_longdata(THD *thd, c
param= stmt->param_array[param_number];
+ Set_longdata_error_handler err_handler(stmt);
+ /*
+ Install handler that will catch any errors that can be generated
+ during execution of Item_param::set_longdata() and propagate
+ them to Statement::last_error.
+ */
+ thd->push_internal_handler(&err_handler);
#ifndef EMBEDDED_LIBRARY
- if (param->set_longdata(packet, (ulong) (packet_end - packet)))
+ param->set_longdata(packet, (ulong) (packet_end - packet));
#else
- if (param->set_longdata(thd->extra_data, thd->extra_length))
+ param->set_longdata(thd->extra_data, thd->extra_length);
#endif
- {
- stmt->state= Query_arena::ERROR;
- stmt->last_errno= ER_OUTOFMEMORY;
- sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
- }
+ thd->pop_internal_handler();
general_log_print(thd, thd->command, NullS);
@@ -3389,6 +3420,13 @@ Prepared_statement::execute_loop(String
bool error;
int reprepare_attempt= 0;
+ /* Check if we got an error when sending long data */
+ if (state == Query_arena::ERROR)
+ {
+ my_message(last_errno, last_error, MYF(0));
+ return TRUE;
+ }
+
if (set_parameters(expanded_query, packet, packet_end))
return TRUE;
@@ -3656,12 +3694,6 @@ bool Prepared_statement::execute(String
status_var_increment(thd->status_var.com_stmt_execute);
- /* Check if we got an error when sending long data */
- if (state == Query_arena::ERROR)
- {
- my_message(last_errno, last_error, MYF(0));
- return TRUE;
- }
if (flags & (uint) IS_IN_USE)
{
my_error(ER_PS_NO_RECURSION, MYF(0));
=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc 2011-02-17 11:53:09 +0000
+++ b/sql/sys_vars.cc 2011-03-11 13:00:21 +0000
@@ -1182,6 +1182,16 @@ static Sys_var_harows Sys_sql_max_join_s
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_max_join_size), DEPRECATED(70000, 0));
+static Sys_var_ulong Sys_max_long_data_size(
+ "max_long_data_size",
+ "The maximum BLOB length to send to server from "
+ "mysql_send_long_data API. Deprecated option; "
+ "use max_allowed_packet instead.",
+ READ_ONLY GLOBAL_VAR(max_long_data_size),
+ CMD_LINE(REQUIRED_ARG, OPT_MAX_LONG_DATA_SIZE),
+ VALID_RANGE(1024, UINT_MAX32), DEFAULT(1024*1024),
+ BLOCK_SIZE(1));
+
static PolyLock_mutex PLock_prepared_stmt_count(&LOCK_prepared_stmt_count);
static Sys_var_ulong Sys_max_prepared_stmt_count(
"max_prepared_stmt_count",
=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c 2011-02-18 14:19:55 +0000
+++ b/tests/mysql_client_test.c 2011-03-11 13:00:21 +0000
@@ -19503,6 +19503,56 @@ static void test_bug57058()
/*
+ Bug #56976: Severe Denial Of Service in prepared statements
+*/
+static void test_bug56976()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[1];
+ int rc;
+ const char* query = "SELECT LENGTH(?)";
+ char *long_buffer;
+ unsigned long i, packet_len = 256 * 1024L;
+ unsigned long dos_len = 2 * 1024 * 1024L;
+
+ DBUG_ENTER("test_bug56976");
+ myheader("test_bug56976");
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_TINY_BLOB;
+
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ long_buffer= (char*) my_malloc(packet_len, MYF(0));
+ DIE_UNLESS(long_buffer);
+
+ memset(long_buffer, 'a', packet_len);
+
+ for (i= 0; i < dos_len / packet_len; i++)
+ {
+ rc= mysql_stmt_send_long_data(stmt, 0, long_buffer, packet_len);
+ check_execute(stmt, rc);
+ }
+
+ my_free(long_buffer);
+ rc= mysql_stmt_execute(stmt);
+
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt) == ER_UNKNOWN_ERROR);
+
+ mysql_stmt_close(stmt);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
Read and parse arguments and MySQL options from my.cnf
*/
@@ -19838,6 +19888,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug47485", test_bug47485 },
{ "test_bug58036", test_bug58036 },
{ "test_bug57058", test_bug57058 },
+ { "test_bug56976", test_bug56976 },
{ 0, 0 }
};
Attachment: [text/bzr-bundle] bzr/dmitry.shulga@oracle.com-20110311130021-8pm72l82k47jb684.bundle
| Thread |
|---|
| • bzr commit into mysql-5.5 branch (Dmitry.Shulga:3362) Bug#11764168 | Dmitry Shulga | 11 Mar |