summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/mysqld--help-notwin.result5
-rw-r--r--mysql-test/r/mysqld--help-win.result5
-rw-r--r--mysql-test/r/variables.result3
-rw-r--r--mysql-test/suite/sys_vars/r/max_long_data_size_basic.result14
-rw-r--r--mysql-test/suite/sys_vars/t/max_long_data_size_basic.test17
-rw-r--r--mysql-test/t/variables.test5
-rw-r--r--sql/item.cc10
-rw-r--r--sql/mysqld.cc17
-rw-r--r--sql/mysqld.h4
-rw-r--r--sql/sql_prepare.cc31
-rw-r--r--sql/sys_vars.cc10
-rw-r--r--tests/mysql_client_test.c51
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 }
};