1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

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

of service in prepared statements).
This commit is contained in:
Dmitry Shulga
2011-03-15 18:57:36 +06:00
12 changed files with 161 additions and 11 deletions

View File

@ -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
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

View File

@ -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
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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
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,
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)
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;
} }

View File

@ -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
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
}; };

View File

@ -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)
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,
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)
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));

View File

@ -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",

View File

@ -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[]= {
{ "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 }
}; };