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.
This commit is contained in:
commit
feb6d22328
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
14
mysql-test/suite/sys_vars/r/max_long_data_size_basic.result
Normal file
14
mysql-test/suite/sys_vars/r/max_long_data_size_basic.result
Normal 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
|
17
mysql-test/suite/sys_vars/t/max_long_data_size_basic.test
Normal file
17
mysql-test/suite/sys_vars/t/max_long_data_size_basic.test
Normal 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;
|
||||
|
@ -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;
|
||||
|
10
sql/item.cc
10
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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",
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user