From: Dmitry Shulga Date: March 11 2011 6:31am Subject: bzr commit into mysql-5.1 branch (Dmitry.Shulga:3613) Bug#11764168 List-Archive: http://lists.mysql.com/commits/132793 X-Bug: 11764168 Message-Id: <201103110632.p2AEHLt3001279@acsinet15.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============5995844659834307772==" --===============5995844659834307772== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///Users/shulga/projects/mysql/mysql-5.1-bug56976/ based on revid:mattias.jonsson@stripped 3613 Dmitry Shulga 2011-03-11 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-11 06:31:14 +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-11 06:31:14 +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-11 06:31:14 +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-11 06:31:14 +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-11 06:31:14 +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-11 06:31:14 +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-11 06:31:14 +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-11 06:31:14 +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 } }; --===============5995844659834307772== 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\ # iq5oypzuk47f7brc # target_branch: file:///Users/shulga/projects/mysql/mysql-5.1-\ # bug56976/ # testament_sha1: 4a61a29e390e9910661c5d4b31c726adedc2552e # timestamp: 2011-03-11 12:31:20 +0600 # base_revision_id: mattias.jonsson@stripped\ # 3vgir6mxnrhv175w # # Begin bundle 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 --===============5995844659834307772==--