A fix and a test case for Bug#16365 "Prepared Statements: DoS with

too many open statements". The patch adds a new global variable
@@max_prepared_stmt_count. This variable limits the total number
of prepared statements in the server. The default value of
@@max_prepared_stmt_count is 16382. 16382 small statements
(a select against 3 tables with GROUP, ORDER and LIMIT) consume 
100MB of RAM. Once this limit has been reached, the server will 
refuse to prepare a new statement and return ER_UNKNOWN_ERROR 
(unfortunately, we can't add new errors to 4.1 without breaking 5.0). The limit is changeable after startup
and can accept any value from 0 to 1 million. In case
the new value of the limit is less than the current
statement count, no new statements can be added, while the old
still can be used. Additionally, the current count of prepared 
statements is now available through a global read-only variable 
@@prepared_stmt_count.


mysql-test/r/ps.result:
  Test results fixed (a test case for Bug#16365)
mysql-test/t/ps.test:
  A test case for Bug#16365 "Prepared Statements: DoS with too many 
  open statements". Also fix statement leaks in other tests.
sql/mysql_priv.h:
  Add declarations for new global variables.
sql/mysqld.cc:
  Add definitions of max_prepared_stmt_count, prepared_stmt_count.
sql/set_var.cc:
  Implement support for @@prepared_stmt_count and 
  @@max_prepared_stmt_count. Currently these variables are queried
  without acquiring LOCK_prepared_stmt_count due to limitations of
  the set_var/sys_var class design. Updates are, however, protected 
  with a lock.
sql/set_var.h:
  New declarations to add support for @@max_prepared_stmt_count.
  Implement a new class, where the lock to be used when updating
  a variable is a parameter.
sql/sql_class.cc:
  Add accounting of the total number of prepared statements in the
  server to the methods of Statement_map.
sql/sql_class.h:
  Add accounting of the total number of prepared statements in the
  server to the methods of Statement_map.
sql/sql_prepare.cc:
  Statement_map::insert will now send a message in case of an
  error.
This commit is contained in:
unknown 2006-04-07 23:37:06 +04:00
commit 5b5530daa5
9 changed files with 464 additions and 60 deletions

View file

@ -47,13 +47,7 @@ public:
#if MYSQL_VERSION_ID < 50000
bool no_support_one_shot;
#endif
sys_var(const char *name_arg)
:name(name_arg), after_update(0)
#if MYSQL_VERSION_ID < 50000
, no_support_one_shot(1)
#endif
{}
sys_var(const char *name_arg,sys_after_update_func func)
sys_var(const char *name_arg, sys_after_update_func func= NULL)
:name(name_arg), after_update(func)
#if MYSQL_VERSION_ID < 50000
, no_support_one_shot(1)
@ -79,15 +73,35 @@ public:
};
class sys_var_long_ptr :public sys_var
/*
A base class for all variables that require its access to
be guarded with a mutex.
*/
class sys_var_global: public sys_var
{
protected:
pthread_mutex_t *guard;
public:
sys_var_global(const char *name_arg, sys_after_update_func after_update_arg,
pthread_mutex_t *guard_arg)
:sys_var(name_arg, after_update_arg), guard(guard_arg) {}
};
/*
A global-only ulong variable that requires its access to be
protected with a mutex.
*/
class sys_var_long_ptr_global: public sys_var_global
{
public:
ulong *value;
sys_var_long_ptr(const char *name_arg, ulong *value_ptr)
:sys_var(name_arg),value(value_ptr) {}
sys_var_long_ptr(const char *name_arg, ulong *value_ptr,
sys_after_update_func func)
:sys_var(name_arg,func), value(value_ptr) {}
sys_var_long_ptr_global(const char *name_arg, ulong *value_ptr,
pthread_mutex_t *guard_arg,
sys_after_update_func after_update_arg= NULL)
:sys_var_global(name_arg, after_update_arg, guard_arg), value(value_ptr) {}
bool check(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
@ -97,6 +111,18 @@ public:
};
/*
A global ulong variable that is protected by LOCK_global_system_variables
*/
class sys_var_long_ptr :public sys_var_long_ptr_global
{
public:
sys_var_long_ptr(const char *name_arg, ulong *value_ptr,
sys_after_update_func after_update_arg= NULL);
};
class sys_var_ulonglong_ptr :public sys_var
{
public:
@ -175,7 +201,7 @@ class sys_var_const_str :public sys_var
public:
char *value; // Pointer to const value
sys_var_const_str(const char *name_arg, const char *value_arg)
:sys_var(name_arg), value((char*) value_arg)
:sys_var(name_arg),value((char*) value_arg)
{}
bool check(THD *thd, set_var *var)
{
@ -221,10 +247,7 @@ public:
class sys_var_thd :public sys_var
{
public:
sys_var_thd(const char *name_arg)
:sys_var(name_arg)
{}
sys_var_thd(const char *name_arg, sys_after_update_func func)
sys_var_thd(const char *name_arg, sys_after_update_func func= NULL)
:sys_var(name_arg,func)
{}
bool check_type(enum_var_type type) { return 0; }