List:Commits« Previous MessageNext Message »
From:Dmitry Shulga Date:March 11 2011 1:02pm
Subject:bzr commit into mysql-5.5 branch (Dmitry.Shulga:3362) Bug#11764168
View as plain text  
#At file:///Users/shulga/projects/mysql/mysql-5.5/ based on revid:marc.alff@stripped

 3362 Dmitry Shulga	2011-03-11 [merge]
      Manual merge from mysql-5.1 for Bug#11764168 (56976: Severe denial
      of service in prepared statements).

    modified:
      mysql-test/r/mysqld--help-notwin.result
      mysql-test/r/mysqld--help-win.result
      mysql-test/r/variables.result
      mysql-test/t/variables.test
      sql/item.cc
      sql/mysqld.cc
      sql/mysqld.h
      sql/sql_prepare.cc
      sql/sys_vars.cc
      tests/mysql_client_test.c
=== modified file 'mysql-test/r/mysqld--help-notwin.result'
--- a/mysql-test/r/mysqld--help-notwin.result	2011-01-14 13:21:46 +0000
+++ b/mysql-test/r/mysqld--help-notwin.result	2011-03-11 13:00:21 +0000
@@ -309,6 +309,10 @@ The following options may be given as th
  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

=== modified file 'mysql-test/r/mysqld--help-win.result'
--- a/mysql-test/r/mysqld--help-win.result	2011-01-14 13:21:46 +0000
+++ b/mysql-test/r/mysqld--help-win.result	2011-03-11 13:00:21 +0000
@@ -308,6 +308,10 @@ The following options may be given as th
  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

=== modified file 'mysql-test/r/variables.result'
--- a/mysql-test/r/variables.result	2011-02-10 07:34:22 +0000
+++ b/mysql-test/r/variables.result	2011-03-11 13:00:21 +0000
@@ -1540,6 +1540,9 @@ ERROR HY000: Cannot drop default keycach
 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;

=== modified file 'mysql-test/t/variables.test'
--- a/mysql-test/t/variables.test	2011-02-10 07:34:22 +0000
+++ b/mysql-test/t/variables.test	2011-03-11 13:00:21 +0000
@@ -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;

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2011-03-04 15:43:28 +0000
+++ b/sql/item.cc	2011-03-11 13:00:21 +0000
@@ -2885,6 +2885,17 @@ bool Item_param::set_longdata(const char
     (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;

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2011-03-10 08:43:55 +0000
+++ b/sql/mysqld.cc	2011-03-11 13:00:21 +0000
@@ -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.
@@ -7155,6 +7161,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;
 }
@@ -7381,6 +7391,13 @@ static int get_options(int *argc_ptr, ch
 
   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;
 }
 

=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h	2011-03-10 08:43:55 +0000
+++ b/sql/mysqld.h	2011-03-11 13:00:21 +0000
@@ -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
 };
 
 

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2011-03-01 14:42:37 +0000
+++ b/sql/sql_prepare.cc	2011-03-11 13:00:21 +0000
@@ -2784,6 +2784,34 @@ void mysql_sql_stmt_close(THD *thd)
   }
 }
 
+
+class Set_longdata_error_handler : public Internal_error_handler
+{
+public:
+  Set_longdata_error_handler(Prepared_statement *statement)
+    : stmt(statement)
+  { }
+
+public:
+  bool handle_condition(THD *thd,
+                        uint sql_errno,
+                        const char* sqlstate,
+                        MYSQL_ERROR::enum_warning_level level,
+                        const char* msg,
+                        MYSQL_ERROR ** cond_hdl)
+  {
+    stmt->state= Query_arena::ERROR;
+    stmt->last_errno= sql_errno;
+    strncpy(stmt->last_error, msg, MYSQL_ERRMSG_SIZE);
+
+    return TRUE;
+  }
+
+private:
+  Prepared_statement *stmt;
+};
+
+
 /**
   Handle long data in pieces from client.
 
@@ -2840,16 +2868,19 @@ void mysql_stmt_get_longdata(THD *thd, c
 
   param= stmt->param_array[param_number];
 
+  Set_longdata_error_handler err_handler(stmt);
+  /*
+    Install handler that will catch any errors that can be generated
+    during execution of Item_param::set_longdata() and propagate
+    them to Statement::last_error.
+  */
+  thd->push_internal_handler(&err_handler);
 #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
-  {
-    stmt->state= Query_arena::ERROR;
-    stmt->last_errno= ER_OUTOFMEMORY;
-    sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
-  }
+  thd->pop_internal_handler();
 
   general_log_print(thd, thd->command, NullS);
 
@@ -3389,6 +3420,13 @@ Prepared_statement::execute_loop(String 
   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 +3694,6 @@ bool Prepared_statement::execute(String 
 
   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));

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2011-02-17 11:53:09 +0000
+++ b/sql/sys_vars.cc	2011-03-11 13:00:21 +0000
@@ -1182,6 +1182,16 @@ static Sys_var_harows Sys_sql_max_join_s
        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",

=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c	2011-02-18 14:19:55 +0000
+++ b/tests/mysql_client_test.c	2011-03-11 13:00:21 +0000
@@ -19503,6 +19503,56 @@ static void test_bug57058()
 
 
 /*
+  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;
+}
+
+
+/*
   Read and parse arguments and MySQL options from my.cnf
 */
 
@@ -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 }
 };
 


Attachment: [text/bzr-bundle] bzr/dmitry.shulga@oracle.com-20110311130021-8pm72l82k47jb684.bundle
Thread
bzr commit into mysql-5.5 branch (Dmitry.Shulga:3362) Bug#11764168Dmitry Shulga11 Mar