From: Dmitry Shulga Date: March 15 2011 1:06pm Subject: bzr push into mysql-5.1 branch (Dmitry.Shulga:3615 to 3616) Bug#11764168 List-Archive: http://lists.mysql.com/commits/133020 X-Bug: 11764168 Message-Id: <201103151306.p2FD6UiO022393@acsmt357.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============0497696135850596165==" --===============0497696135850596165== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline 3616 Dmitry Shulga 2011-03-15 Fixed Bug#11764168 "56976: SEVERE DENIAL OF SERVICE IN PREPARED STATEMENTS". The problem was that server didn't check resulting size of prepared statement argument which was set using mysql_send_long_data() API. By calling mysql_send_long_data() several times it was possible to create overly big string and thus force server to allocate memory for it. There was no way to limit this allocation. The solution is to add check for size of result string against value of max_long_data_size start-up parameter. When intermediate string exceeds max_long_data_size value an appropriate error message is emitted. We can't use existing max_allowed_packet parameter for this purpose since its value is limited by 1GB and therefore using it as a limit for data set through mysql_send_long_data() API would have been an incompatible change. Newly introduced max_long_data_size parameter gets value from max_allowed_packet parameter unless its value is specified explicitly. This new parameter is marked as deprecated and will be eventually replaced by max_allowed_packet parameter. Value of max_long_data_size parameter can be set only at server startup. @ mysql-test/t/variables.test Added checking for new start-up parameter max_long_data_size. @ sql/item.cc Added call to my_message() when accumulated string exceeds max_long_data_size value. my_message() calls error handler that was installed in mysql_stmt_get_longdata before call to Item_param::set_longdata. The error handler then sets state, last_error and last_errno fields for current statement to values which correspond to error which was caught. @ sql/mysql_priv.h Added max_long_data_size variable declaration. @ sql/mysqld.cc Added support for start-up parameter 'max_long_data_size'. This parameter limits size of data which can be sent from client to server using mysql_send_long_data() API. @ sql/set_var.cc Added variable 'max_long_data_size' into list of variables displayed by command 'show variables'. @ sql/sql_prepare.cc Added error handler class Set_longdata_error_handler. This handler is used to catch any errors that can be generated during execution of Item_param::set_longdata(). Source code snippet that makes checking for statement's state during statement execution is moved from Prepared_statement::execute() to Prepared_statement::execute_loop() in order not to call set_parameters() when statement has failed during set_long_data() execution. If this hadn't been done the call to set_parameters() would have failed. @ tests/mysql_client_test.c A testcase for the bug #56976 was added. modified: mysql-test/r/variables.result mysql-test/t/variables.test sql/item.cc sql/mysql_priv.h sql/mysqld.cc sql/set_var.cc sql/sql_prepare.cc tests/mysql_client_test.c 3615 Davi Arnaut 2011-03-14 Bug#11765202: Dbug_violation_helper::~Dbug_violation_helper(): Assertion `!_entered' failed. Add a missing DBUG_RETURN function test_if_number(). modified: mysql-test/r/analyse.result mysql-test/t/analyse.test sql/sql_analyse.cc === modified file 'mysql-test/r/variables.result' --- a/mysql-test/r/variables.result 2010-11-25 03:11:05 +0000 +++ b/mysql-test/r/variables.result 2011-03-15 11:36:12 +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-02 17:05:28 +0000 +++ b/mysql-test/t/variables.test 2011-03-15 11:36:12 +0000 @@ -1293,6 +1293,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-02-22 21:03:32 +0000 +++ b/sql/item.cc 2011-03-15 11:36:12 +0000 @@ -2742,6 +2742,16 @@ 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/mysql_priv.h' --- a/sql/mysql_priv.h 2011-02-22 21:03:32 +0000 +++ b/sql/mysql_priv.h 2011-03-15 11:36:12 +0000 @@ -1965,6 +1965,7 @@ extern my_bool relay_log_purge, opt_inno extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version, mysqld_port, dropping_tables; extern uint delay_key_write_options; +extern ulong max_long_data_size; #endif /* MYSQL_SERVER */ #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern MYSQL_PLUGIN_IMPORT uint lower_case_table_names; === modified file 'sql/mysqld.cc' --- a/sql/mysqld.cc 2011-02-04 04:47:46 +0000 +++ b/sql/mysqld.cc 2011-03-15 11:36:12 +0000 @@ -409,6 +409,7 @@ TYPELIB log_output_typelib= {array_eleme /* 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; static bool volatile ready_to_exit; static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; @@ -574,6 +575,11 @@ ulong delayed_insert_errors,flush_time; ulong specialflag=0; ulong binlog_cache_use= 0, binlog_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; uint max_user_connections= 0; /** Limit of the total number of prepared statements in the server. @@ -5800,7 +5806,8 @@ enum options_mysqld OPT_SLOW_QUERY_LOG_FILE, OPT_IGNORE_BUILTIN_INNODB, OPT_BINLOG_DIRECT_NON_TRANS_UPDATE, - OPT_DEFAULT_CHARACTER_SET_OLD + OPT_DEFAULT_CHARACTER_SET_OLD, + OPT_MAX_LONG_DATA_SIZE }; @@ -6880,6 +6887,13 @@ thread is in the relay logs.", &global_system_variables.max_length_for_sort_data, &max_system_variables.max_length_for_sort_data, 0, GET_ULONG, REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, + {"max_long_data_size", OPT_MAX_LONG_DATA_SIZE, + "The maximum size of prepared statement parameter which can be provided " + "through mysql_send_long_data() API call. " + "Deprecated option; use max_allowed_packet instead.", + &max_long_data_size, + &max_long_data_size, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L, 1024, UINT_MAX32, MALLOC_OVERHEAD, 1, 0}, {"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT, "Maximum number of prepared statements in the server.", &max_prepared_stmt_count, &max_prepared_stmt_count, @@ -8688,6 +8702,10 @@ mysqld_get_one_option(int optid, } break; #endif /* defined(ENABLED_DEBUG_SYNC) */ + case OPT_MAX_LONG_DATA_SIZE: + max_long_data_size_used= true; + WARN_DEPRECATED(NULL, VER_CELOSIA, "--max_long_data_size", "--max_allowed_packet"); + break; } return 0; } @@ -8849,6 +8867,14 @@ static int get_options(int *argc,char ** else pool_of_threads_scheduler(&thread_scheduler); /* purecov: tested */ #endif + + /* + 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/set_var.cc' --- a/sql/set_var.cc 2011-02-18 13:12:36 +0000 +++ b/sql/set_var.cc 2011-03-15 11:36:12 +0000 @@ -394,6 +394,12 @@ static sys_var_thd_ulong sys_max_seeks_f &SV::max_seeks_for_key); static sys_var_thd_ulong sys_max_length_for_sort_data(&vars, "max_length_for_sort_data", &SV::max_length_for_sort_data); +static sys_var_const sys_max_long_data_size(&vars, + "max_long_data_size", + OPT_GLOBAL, SHOW_LONG, + (uchar*) + &max_long_data_size); + #ifndef TO_BE_DELETED /* Alias for max_join_size */ static sys_var_thd_ha_rows sys_sql_max_join_size(&vars, "sql_max_join_size", &SV::max_join_size, === modified file 'sql/sql_prepare.cc' --- a/sql/sql_prepare.cc 2011-02-22 21:03:32 +0000 +++ b/sql/sql_prepare.cc 2011-03-15 11:36:12 +0000 @@ -2730,6 +2730,32 @@ 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_error(uint sql_errno, + const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *) + { + stmt->state= Query_arena::ERROR; + stmt->last_errno= sql_errno; + strncpy(stmt->last_error, message, MYSQL_ERRMSG_SIZE); + + return TRUE; + } + +private: + Prepared_statement *stmt; +}; + + /** Handle long data in pieces from client. @@ -2786,16 +2812,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); @@ -3257,6 +3286,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; @@ -3497,12 +3533,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 'tests/mysql_client_test.c' --- a/tests/mysql_client_test.c 2011-02-18 14:17:37 +0000 +++ b/tests/mysql_client_test.c 2011-03-15 11:36:12 +0000 @@ -18465,6 +18465,56 @@ static void test_bug58036() /* + 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, MYF(0)); + 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 */ @@ -18791,6 +18841,7 @@ static struct my_tests_st my_tests[]= { { "test_bug54041", test_bug54041 }, { "test_bug47485", test_bug47485 }, { "test_bug58036", test_bug58036 }, + { "test_bug56976", test_bug56976 }, { 0, 0 } }; --===============0497696135850596165== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/dmitry.shulga@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: dmitry.shulga@stripped\ # m30nf2ek82djql1g # target_branch: file:///Users/shulga/projects/mysql/mysql-5.1-\ # bug56976/ # testament_sha1: aee24c3f403d3783ab776534a14a6d04ddedb6d0 # timestamp: 2011-03-15 19:06:25 +0600 # base_revision_id: davi.arnaut@stripped\ # hz725et87kuobcil # # Begin bundle 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 --===============0497696135850596165==--