Commit b138b0c9 authored by Dmitry Shulga's avatar Dmitry Shulga

Manual merge from mysql-5.1 for Bug#11764168 (56976: Severe denial

of service in prepared statements).

sql/sql_prepare.cc:
  At mysql_stmt_get_longdata(): instead of pushing an internal
  error handler (as done in 5.1-tree) we save, set and restore
  the statement's diagnostics area and warning info.
parents aa7591ff 0dd27502
...@@ -309,6 +309,10 @@ The following options may be given as the first argument: ...@@ -309,6 +309,10 @@ The following options may be given as the first argument:
max_join_size records return an error max_join_size records return an error
--max-length-for-sort-data=# --max-length-for-sort-data=#
Max number of bytes in sorted records 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=# --max-prepared-stmt-count=#
Maximum number of prepared statements in the server Maximum number of prepared statements in the server
--max-relay-log-size=# --max-relay-log-size=#
...@@ -830,6 +834,7 @@ max-error-count 64 ...@@ -830,6 +834,7 @@ max-error-count 64
max-heap-table-size 16777216 max-heap-table-size 16777216
max-join-size 18446744073709551615 max-join-size 18446744073709551615
max-length-for-sort-data 1024 max-length-for-sort-data 1024
max-long-data-size 1048576
max-prepared-stmt-count 16382 max-prepared-stmt-count 16382
max-relay-log-size 0 max-relay-log-size 0
max-seeks-for-key 18446744073709551615 max-seeks-for-key 18446744073709551615
......
...@@ -308,6 +308,10 @@ The following options may be given as the first argument: ...@@ -308,6 +308,10 @@ The following options may be given as the first argument:
max_join_size records return an error max_join_size records return an error
--max-length-for-sort-data=# --max-length-for-sort-data=#
Max number of bytes in sorted records 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=# --max-prepared-stmt-count=#
Maximum number of prepared statements in the server Maximum number of prepared statements in the server
--max-relay-log-size=# --max-relay-log-size=#
...@@ -833,6 +837,7 @@ max-error-count 64 ...@@ -833,6 +837,7 @@ max-error-count 64
max-heap-table-size 16777216 max-heap-table-size 16777216
max-join-size 18446744073709551615 max-join-size 18446744073709551615
max-length-for-sort-data 1024 max-length-for-sort-data 1024
max-long-data-size 1048576
max-prepared-stmt-count 16382 max-prepared-stmt-count 16382
max-relay-log-size 0 max-relay-log-size 0
max-seeks-for-key 18446744073709551615 max-seeks-for-key 18446744073709551615
......
...@@ -1540,6 +1540,9 @@ ERROR HY000: Cannot drop default keycache ...@@ -1540,6 +1540,9 @@ ERROR HY000: Cannot drop default keycache
SET @@global.key_cache_block_size=0; SET @@global.key_cache_block_size=0;
Warnings: Warnings:
Warning 1292 Truncated incorrect key_cache_block_size value: '0' 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_binlog_cache_size=DEFAULT;
SET @@global.max_join_size=DEFAULT; SET @@global.max_join_size=DEFAULT;
SET @@global.key_buffer_size=@kbs; SET @@global.key_buffer_size=@kbs;
......
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
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;
...@@ -1281,6 +1281,11 @@ SET @@global.max_join_size=0; ...@@ -1281,6 +1281,11 @@ SET @@global.max_join_size=0;
SET @@global.key_buffer_size=0; SET @@global.key_buffer_size=0;
SET @@global.key_cache_block_size=0; SET @@global.key_cache_block_size=0;
#
# Bug#56976: added new start-up parameter
#
select @@max_long_data_size;
# cleanup # cleanup
SET @@global.max_binlog_cache_size=DEFAULT; SET @@global.max_binlog_cache_size=DEFAULT;
SET @@global.max_join_size=DEFAULT; SET @@global.max_join_size=DEFAULT;
......
...@@ -2885,6 +2885,16 @@ bool Item_param::set_longdata(const char *str, ulong length) ...@@ -2885,6 +2885,16 @@ bool Item_param::set_longdata(const char *str, ulong length)
(here), and first have to concatenate all pieces together, (here), and first have to concatenate all pieces together,
write query to the binary log and only then perform conversion. 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)) if (str_value.append(str, length, &my_charset_bin))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
state= LONG_DATA_VALUE; state= LONG_DATA_VALUE;
......
...@@ -324,6 +324,7 @@ static PSI_rwlock_key key_rwlock_openssl; ...@@ -324,6 +324,7 @@ static PSI_rwlock_key key_rwlock_openssl;
/* the default log output is log tables */ /* the default log output is log tables */
static bool lower_case_table_names_used= 0; 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 select_thread_in_use, signal_thread_in_use;
/* See Bug#56666 and Bug#56760 */; /* See Bug#56666 and Bug#56760 */;
volatile bool ready_to_exit; volatile bool ready_to_exit;
...@@ -478,6 +479,11 @@ ulong specialflag=0; ...@@ -478,6 +479,11 @@ ulong specialflag=0;
ulong binlog_cache_use= 0, binlog_cache_disk_use= 0; ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0; ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
ulong max_connections, max_connect_errors; 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. Limit of the total number of prepared statements in the server.
Is necessary to protect the server against out-of-memory attacks. Is necessary to protect the server against out-of-memory attacks.
...@@ -7160,6 +7166,10 @@ mysqld_get_one_option(int optid, ...@@ -7160,6 +7166,10 @@ mysqld_get_one_option(int optid,
if (argument == NULL) /* no argument */ if (argument == NULL) /* no argument */
log_error_file_ptr= const_cast<char*>(""); log_error_file_ptr= const_cast<char*>("");
break; 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; return 0;
} }
...@@ -7386,6 +7396,13 @@ static int get_options(int *argc_ptr, char ***argv_ptr) ...@@ -7386,6 +7396,13 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
opt_readonly= read_only; 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; return 0;
} }
......
...@@ -126,6 +126,7 @@ extern char *default_storage_engine; ...@@ -126,6 +126,7 @@ extern char *default_storage_engine;
extern bool opt_endinfo, using_udf_functions; extern bool opt_endinfo, using_udf_functions;
extern my_bool locked_in_memory; extern my_bool locked_in_memory;
extern bool opt_using_transactions; extern bool opt_using_transactions;
extern ulong max_long_data_size;
extern ulong current_pid; extern ulong current_pid;
extern ulong expire_logs_days; extern ulong expire_logs_days;
extern my_bool relay_log_recovery; extern my_bool relay_log_recovery;
...@@ -397,7 +398,8 @@ enum options_mysqld ...@@ -397,7 +398,8 @@ enum options_mysqld
OPT_UPDATE_LOG, OPT_UPDATE_LOG,
OPT_WANT_CORE, OPT_WANT_CORE,
OPT_ENGINE_CONDITION_PUSHDOWN, OPT_ENGINE_CONDITION_PUSHDOWN,
OPT_LOG_ERROR OPT_LOG_ERROR,
OPT_MAX_LONG_DATA_SIZE
}; };
......
...@@ -2784,6 +2784,7 @@ void mysql_sql_stmt_close(THD *thd) ...@@ -2784,6 +2784,7 @@ void mysql_sql_stmt_close(THD *thd)
} }
} }
/** /**
Handle long data in pieces from client. Handle long data in pieces from client.
...@@ -2840,16 +2841,25 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) ...@@ -2840,16 +2841,25 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
param= stmt->param_array[param_number]; 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 #ifndef EMBEDDED_LIBRARY
if (param->set_longdata(packet, (ulong) (packet_end - packet))) param->set_longdata(packet, (ulong) (packet_end - packet));
#else #else
if (param->set_longdata(thd->extra_data, thd->extra_length)) param->set_longdata(thd->extra_data, thd->extra_length);
#endif #endif
if (thd->stmt_da->is_error())
{ {
stmt->state= Query_arena::ERROR; stmt->state= Query_arena::ERROR;
stmt->last_errno= ER_OUTOFMEMORY; stmt->last_errno= thd->stmt_da->sql_errno();
sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0); 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); general_log_print(thd, thd->command, NullS);
...@@ -3389,6 +3399,13 @@ Prepared_statement::execute_loop(String *expanded_query, ...@@ -3389,6 +3399,13 @@ Prepared_statement::execute_loop(String *expanded_query,
bool error; bool error;
int reprepare_attempt= 0; 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)) if (set_parameters(expanded_query, packet, packet_end))
return TRUE; return TRUE;
...@@ -3656,12 +3673,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) ...@@ -3656,12 +3673,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
status_var_increment(thd->status_var.com_stmt_execute); 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) if (flags & (uint) IS_IN_USE)
{ {
my_error(ER_PS_NO_RECURSION, MYF(0)); my_error(ER_PS_NO_RECURSION, MYF(0));
......
...@@ -1182,6 +1182,16 @@ static Sys_var_harows Sys_sql_max_join_size( ...@@ -1182,6 +1182,16 @@ static Sys_var_harows Sys_sql_max_join_size(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_max_join_size), DEPRECATED(70000, 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 PolyLock_mutex PLock_prepared_stmt_count(&LOCK_prepared_stmt_count);
static Sys_var_ulong Sys_max_prepared_stmt_count( static Sys_var_ulong Sys_max_prepared_stmt_count(
"max_prepared_stmt_count", "max_prepared_stmt_count",
......
...@@ -19464,6 +19464,56 @@ static void test_bug49972() ...@@ -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. Bug#57058 SERVER_QUERY_WAS_SLOW not wired up.
*/ */
...@@ -19838,6 +19888,7 @@ static struct my_tests_st my_tests[]= { ...@@ -19838,6 +19888,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug47485", test_bug47485 }, { "test_bug47485", test_bug47485 },
{ "test_bug58036", test_bug58036 }, { "test_bug58036", test_bug58036 },
{ "test_bug57058", test_bug57058 }, { "test_bug57058", test_bug57058 },
{ "test_bug56976", test_bug56976 },
{ 0, 0 } { 0, 0 }
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment