From: Dmitry Shulga Date: February 11 2011 5:48am Subject: bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3537) Bug#56976 List-Archive: http://lists.mysql.com/commits/131100 X-Bug: 56976 Message-Id: <201102110550.p1B4sdp2012592@acsinet15.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============5944762025876781917==" --===============5944762025876781917== 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-02-11 Fixed the bug#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 constant. When intermediate string exceeds max_long_data_size value ER_UNKNOWN_ERROR error is emitted. @ 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 sets to corresponding value the next current statement fileds: state, last_error, last_errno. @ sql/item.h Added argument of type THD* to declaration of set_longdata(). @ sql/mysql_priv.h Added max_long_data_size variable declaration. @ sql/mysqld.cc Added support for configuration parameter 'max_long_data_size'. This parameter used to limit size of BLOB data that send from client to server over call to 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 installed inside mysql_stmt_get_longdata() before call to param->set_longdata and deinstalled after return from it. Sourcecode snippet that makes checking for statement's state during statement execution moved from Prepared_statement::execute() to Prepared_statement::execute_loop() in order not to call set_parameters() when statement has been failed during set_long_data() execution. If it didn't do then call to set_parameters would failed. @ tests/mysql_client_test.c It was added testcase for the bug #56976. modified: sql/item.cc sql/item.h sql/mysql_priv.h sql/mysqld.cc sql/set_var.cc sql/sql_prepare.cc tests/mysql_client_test.c === modified file 'sql/item.cc' --- a/sql/item.cc 2010-12-21 11:34:11 +0000 +++ b/sql/item.cc 2011-02-11 05:48:13 +0000 @@ -2729,7 +2729,7 @@ bool Item_param::set_str(const char *str } -bool Item_param::set_longdata(const char *str, ulong length) +bool Item_param::set_longdata(THD *thd, const char *str, ulong length) { DBUG_ENTER("Item_param::set_longdata"); @@ -2742,6 +2742,15 @@ 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, + "Result string 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/item.h' --- a/sql/item.h 2010-12-28 23:47:05 +0000 +++ b/sql/item.h 2011-02-11 05:48:13 +0000 @@ -1687,7 +1687,7 @@ public: void set_double(double i); void set_decimal(const char *str, ulong length); bool set_str(const char *str, ulong length); - bool set_longdata(const char *str, ulong length); + bool set_longdata(THD *thd, const char *str, ulong length); void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg); bool set_from_user_var(THD *thd, const user_var_entry *entry); void reset(); === modified file 'sql/mysql_priv.h' --- a/sql/mysql_priv.h 2010-12-16 19:11:08 +0000 +++ b/sql/mysql_priv.h 2011-02-11 05:48:13 +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-02-11 05:48:13 +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,7 @@ 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; +ulong max_long_data_size; uint max_user_connections= 0; /** Limit of the total number of prepared statements in the server. @@ -5691,7 +5693,7 @@ enum options_mysqld OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS, OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE, - OPT_MAX_JOIN_SIZE, OPT_MAX_PREPARED_STMT_COUNT, + OPT_MAX_JOIN_SIZE, OPT_MAX_LONG_DATA_SIZE, OPT_MAX_PREPARED_STMT_COUNT, OPT_MAX_RELAY_LOG_SIZE, OPT_MAX_SORT_LENGTH, OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, OPT_MAX_LENGTH_FOR_SORT_DATA, @@ -6862,6 +6864,12 @@ 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 BLOB length to send to server from mysql_send_long_data API. " + "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 +8678,11 @@ mysqld_get_one_option(int optid, } break; #endif /* defined(ENABLED_DEBUG_SYNC) */ + case OPT_MAX_LONG_DATA_SIZE: + max_long_data_size= atol(argument); + max_long_data_size_used= true; + WARN_DEPRECATED(NULL, VER_CELOSIA, "--max_long_data_size", "--max_allowed_packet"); + break; } return 0; } @@ -8831,6 +8844,10 @@ static int get_options(int *argc,char ** else pool_of_threads_scheduler(&thread_scheduler); /* purecov: tested */ #endif + + 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-02-11 05:48:13 +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_INT, + (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-02-11 05:48:13 +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 *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,14 @@ void mysql_stmt_get_longdata(THD *thd, c param= stmt->param_array[param_number]; + Set_longdata_error_handler err_handler(stmt); + thd->push_internal_handler(&err_handler); #ifndef EMBEDDED_LIBRARY - if (param->set_longdata(packet, (ulong) (packet_end - packet))) + param->set_longdata(thd, packet, (ulong) (packet_end - packet)); #else - if (param->set_longdata(thd->extra_data, thd->extra_length)) + param->set_longdata(thd, 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 +3281,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 +3528,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-02-11 05:48:13 +0000 @@ -18399,6 +18399,54 @@ 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= malloc(packet_len); + 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); + } + + 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 +18772,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 } }; --===============5944762025876781917== 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\ # 5eug40b91hdx20dj # target_branch: file:///Users/shulga/projects/mysql/5.1-bugteam-\ # bug56976/ # testament_sha1: eeee03738ca09c6b51ba99d726aa2c696a2c9a0d # timestamp: 2011-02-11 11:48:24 +0600 # base_revision_id: dao-gang.qu@stripped\ # 32f4p072xij1zkmj # # Begin bundle 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 GIH7Jf/F3JFOFCQOY+uaAA== --===============5944762025876781917==--