From: Dmitry Shulga Date: March 4 2011 11:02am Subject: bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3537) Bug#11764168 List-Archive: http://lists.mysql.com/commits/132447 X-Bug: 11764168 Message-Id: <201103041102.p2495tUB013397@rcsinet15.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============3006830588135686715==" --===============3006830588135686715== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///Users/shulga/projects/mysql/5.1-bugteam-bug56976/ based on revid:dao-gang.qu@stripped 3537 Dmitry Shulga 2011-03-04 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 === 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-04 11:02:26 +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 2010-11-25 03:11:05 +0000 +++ b/mysql-test/t/variables.test 2011-03-04 11:02:26 +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 2010-12-21 11:34:11 +0000 +++ b/sql/item.cc 2011-03-04 11:02:26 +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 2010-12-16 19:11:08 +0000 +++ b/sql/mysql_priv.h 2011-03-04 11:02:26 +0000 @@ -1969,6 +1969,7 @@ extern MYSQL_PLUGIN_IMPORT uint lower_ca extern bool opt_endinfo, using_udf_functions; extern my_bool locked_in_memory; extern bool opt_using_transactions; +extern ulong max_long_data_size; #endif /* MYSQL_SERVER */ #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern MYSQL_PLUGIN_IMPORT bool mysqld_embedded; === modified file 'sql/mysqld.cc' --- a/sql/mysqld.cc 2010-12-28 23:47:05 +0000 +++ b/sql/mysqld.cc 2011-03-04 11:02:26 +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. @@ -5782,7 +5788,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 }; @@ -6862,6 +6869,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, @@ -8670,6 +8684,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; } @@ -8831,6 +8849,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 2010-12-28 23:47:05 +0000 +++ b/sql/set_var.cc 2011-03-04 11:02:26 +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 2010-12-28 23:47:05 +0000 +++ b/sql/sql_prepare.cc 2011-03-04 11:02:26 +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 2010-12-28 23:47:05 +0000 +++ b/tests/mysql_client_test.c 2011-03-04 11:02:26 +0000 @@ -18399,6 +18399,57 @@ static void test_bug47485() /* + 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 */ @@ -18724,6 +18775,7 @@ static struct my_tests_st my_tests[]= { { "test_bug42373", test_bug42373 }, { "test_bug54041", test_bug54041 }, { "test_bug47485", test_bug47485 }, + { "test_bug56976", test_bug56976 }, { 0, 0 } }; --===============3006830588135686715== 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\ # inshmcncf8zhz4kz # target_branch: file:///Users/shulga/projects/mysql/5.1-bugteam-\ # bug56976/ # testament_sha1: 15b7bfdd195dc80689aba413fa42c5785726b1de # timestamp: 2011-03-04 17:02:33 +0600 # base_revision_id: dao-gang.qu@stripped\ # 32f4p072xij1zkmj # # Begin bundle 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 --===============3006830588135686715==--