List:Commits« Previous MessageNext Message »
From:Dmitry Shulga Date:March 15 2011 1:06pm
Subject:bzr push into mysql-5.5 branch (Dmitry.Shulga:3369 to 3370) Bug#11764168
View as plain text  
 3370 Dmitry Shulga	2011-03-15 [merge]
      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.

    added:
      mysql-test/suite/sys_vars/r/max_long_data_size_basic.result
      mysql-test/suite/sys_vars/t/max_long_data_size_basic.test
    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
 3369 Jon Olav Hauglid	2011-03-15
      Bug #11765416 (former 58381)
      FAILED DROP DATABASE CAN BREAK STATEMENT BASED REPLICATION
      
      The first phase of DROP DATABASE is to delete the tables in the database.
      If deletion of one or more of the tables fail (e.g. due to a FOREIGN KEY
      constraint), DROP DATABASE will be aborted. However, some tables could
      still have been deleted. The problem was that nothing would be written
      to the binary log in this case, so any slaves would not delete these tables.
      Therefore the master and the slaves would get out of sync.
      
      This patch fixes the problem by making sure that DROP TABLE is written
      to the binary log for the tables that were in fact deleted by the failed
      DROP DATABASE statement.
      
      Test case added to binlog.binlog_database.test.

    modified:
      mysql-test/extra/binlog_tests/database.test
      mysql-test/suite/binlog/r/binlog_database.result
      sql/sql_db.cc
=== 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-15 12:57:36 +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-15 12:57:36 +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-15 12:57:36 +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;

=== added file 'mysql-test/suite/sys_vars/r/max_long_data_size_basic.result'
--- a/mysql-test/suite/sys_vars/r/max_long_data_size_basic.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/r/max_long_data_size_basic.result	2011-03-15 12:57:36 +0000
@@ -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

=== added file 'mysql-test/suite/sys_vars/t/max_long_data_size_basic.test'
--- a/mysql-test/suite/sys_vars/t/max_long_data_size_basic.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/max_long_data_size_basic.test	2011-03-15 12:57:36 +0000
@@ -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;
+

=== 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-15 12:57:36 +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-08 17:39:25 +0000
+++ b/sql/item.cc	2011-03-15 12:57:36 +0000
@@ -2885,6 +2885,16 @@ 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-14 13:03:48 +0000
+++ b/sql/mysqld.cc	2011-03-15 12:57:36 +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.
@@ -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, 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-15 12:57:36 +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-08 17:39:25 +0000
+++ b/sql/sql_prepare.cc	2011-03-15 12:57:36 +0000
@@ -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, c
 
   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 
   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 
 
   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-15 12:57:36 +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-15 12:57:36 +0000
@@ -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 }
 };
 


Attachment: [text/bzr-bundle] bzr/dmitry.shulga@oracle.com-20110315125736-kq4hehzxckulrk3z.bundle
Thread
bzr push into mysql-5.5 branch (Dmitry.Shulga:3369 to 3370) Bug#11764168Dmitry Shulga15 Mar