diff options
-rw-r--r-- | mysql-test/r/mysqld--help-notwin.result | 5 | ||||
-rw-r--r-- | mysql-test/r/mysqld--help-win.result | 5 | ||||
-rw-r--r-- | mysql-test/r/variables.result | 3 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/r/max_long_data_size_basic.result | 14 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/t/max_long_data_size_basic.test | 17 | ||||
-rw-r--r-- | mysql-test/t/variables.test | 5 | ||||
-rw-r--r-- | sql/item.cc | 10 | ||||
-rw-r--r-- | sql/mysqld.cc | 17 | ||||
-rw-r--r-- | sql/mysqld.h | 4 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 31 | ||||
-rw-r--r-- | sql/sys_vars.cc | 10 | ||||
-rw-r--r-- | tests/mysql_client_test.c | 51 |
12 files changed, 161 insertions, 11 deletions
diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 6659a291e0b..b4faa833c1b 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -309,6 +309,10 @@ The following options may be given as the first argument: 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 diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index 240c29294e2..361d30620f7 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -308,6 +308,10 @@ The following options may be given as the first argument: 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 diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index 5d6caf21182..96db86262c2 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -1540,6 +1540,9 @@ ERROR HY000: Cannot drop default keycache 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; diff --git a/mysql-test/suite/sys_vars/r/max_long_data_size_basic.result b/mysql-test/suite/sys_vars/r/max_long_data_size_basic.result new file mode 100644 index 00000000000..679a0d3611f --- /dev/null +++ b/mysql-test/suite/sys_vars/r/max_long_data_size_basic.result @@ -0,0 +1,14 @@ +select @@global.max_long_data_size=20; +@@global.max_long_data_size=20 +0 +select @@session.max_long_data_size; +ERROR HY000: Variable 'max_long_data_size' is a GLOBAL variable +SELECT @@global.max_long_data_size = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='max_long_data_size'; +@@global.max_long_data_size = VARIABLE_VALUE +1 +set global max_long_data_size=1; +ERROR HY000: Variable 'max_long_data_size' is a read only variable +set session max_long_data_size=1; +ERROR HY000: Variable 'max_long_data_size' is a read only variable diff --git a/mysql-test/suite/sys_vars/t/max_long_data_size_basic.test b/mysql-test/suite/sys_vars/t/max_long_data_size_basic.test new file mode 100644 index 00000000000..eefa61bd4b7 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/max_long_data_size_basic.test @@ -0,0 +1,17 @@ +select @@global.max_long_data_size=20; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.max_long_data_size; + +# Show that value of the variable matches the value in the GLOBAL I_S table +SELECT @@global.max_long_data_size = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='max_long_data_size'; + +# +# show that it's read-only +# +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set global max_long_data_size=1; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set session max_long_data_size=1; + diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index 2b20bbdc13b..173f8aeaa58 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -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; diff --git a/sql/item.cc b/sql/item.cc index 2090a1e4eda..e930143edfa 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2885,6 +2885,16 @@ bool Item_param::set_longdata(const char *str, ulong length) (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; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 24ce134b40d..154ddbed8fb 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -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. @@ -7160,6 +7166,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; } @@ -7386,6 +7396,13 @@ static int get_options(int *argc_ptr, char ***argv_ptr) 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; } diff --git a/sql/mysqld.h b/sql/mysqld.h index 7fa09ba77ac..13ca52596ce 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -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 }; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 64f252d11d3..9085d018dfd 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2784,6 +2784,7 @@ void mysql_sql_stmt_close(THD *thd) } } + /** Handle long data in pieces from client. @@ -2840,16 +2841,25 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) param= stmt->param_array[param_number]; + Diagnostics_area new_stmt_da, *save_stmt_da= thd->stmt_da; + Warning_info new_warnning_info(thd->query_id), *save_warinig_info= thd->warning_info; + + thd->stmt_da= &new_stmt_da; + thd->warning_info= &new_warnning_info; + #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 + if (thd->stmt_da->is_error()) { stmt->state= Query_arena::ERROR; - stmt->last_errno= ER_OUTOFMEMORY; - sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0); + stmt->last_errno= thd->stmt_da->sql_errno(); + strncpy(stmt->last_error, thd->stmt_da->message(), MYSQL_ERRMSG_SIZE); } + thd->stmt_da= save_stmt_da; + thd->warning_info= save_warinig_info; general_log_print(thd, thd->command, NullS); @@ -3389,6 +3399,13 @@ Prepared_statement::execute_loop(String *expanded_query, 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 +3673,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) 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)); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 612ce48de0b..8f9b91f0f92 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1182,6 +1182,16 @@ static Sys_var_harows Sys_sql_max_join_size( 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", diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 9c06ac0a4ad..7911393758c 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -19464,6 +19464,56 @@ static void test_bug49972() } +/* + 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; +} + + /** Bug#57058 SERVER_QUERY_WAS_SLOW not wired up. */ @@ -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 } }; |