mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-15089 Ensure that connection ID is in 32bit range
This commit is contained in:
23
mysql-test/r/thread_id_overflow.result
Normal file
23
mysql-test/r/thread_id_overflow.result
Normal file
@ -0,0 +1,23 @@
|
||||
connect con1, localhost,root;
|
||||
disconnect con1;
|
||||
connect con2, localhost,root;
|
||||
connection con2;
|
||||
connection default;
|
||||
SET @orig_debug=@@debug_dbug;
|
||||
SET GLOBAL DEBUG_DBUG='+d,thread_id_overflow';
|
||||
connect con3, localhost,root;
|
||||
connection con3;
|
||||
SELECT CONNECTION_ID();
|
||||
CONNECTION_ID()
|
||||
4294967294
|
||||
connection default;
|
||||
SET GLOBAL DEBUG_DBUG=@orig_debug;
|
||||
connect con4, localhost,root;
|
||||
connection con4;
|
||||
select IF(connection_id() - max_id = 1,'Good','Bad') as result;
|
||||
result
|
||||
Good
|
||||
disconnect con4;
|
||||
disconnect con3;
|
||||
disconnect con2;
|
||||
connection default;
|
35
mysql-test/t/thread_id_overflow.test
Normal file
35
mysql-test/t/thread_id_overflow.test
Normal file
@ -0,0 +1,35 @@
|
||||
# test 32bit overflow with connection id
|
||||
--source include/have_debug.inc
|
||||
|
||||
|
||||
# Connect and disconnect once, to create a "hole" in connection ids
|
||||
connect con1, localhost,root;
|
||||
disconnect con1;
|
||||
|
||||
connect con2, localhost,root;
|
||||
connection con2;
|
||||
|
||||
connection default;
|
||||
let $max_id = `SELECT MAX(ID) FROM INFORMATION_SCHEMA.PROCESSLIST`;
|
||||
|
||||
SET @orig_debug=@@debug_dbug;
|
||||
SET GLOBAL DEBUG_DBUG='+d,thread_id_overflow';
|
||||
|
||||
connect con3, localhost,root;
|
||||
connection con3;
|
||||
# next line gives UINT32_MAX -1;
|
||||
SELECT CONNECTION_ID();
|
||||
|
||||
connection default;
|
||||
SET GLOBAL DEBUG_DBUG=@orig_debug;
|
||||
|
||||
connect con4, localhost,root;
|
||||
connection con4;
|
||||
--replace_result $max_id max_id
|
||||
eval select IF(connection_id() - $max_id = 1,'Good','Bad') as result;
|
||||
|
||||
disconnect con4;
|
||||
disconnect con3;
|
||||
disconnect con2;
|
||||
|
||||
connection default;
|
109
sql/mysqld.cc
109
sql/mysqld.cc
@ -559,7 +559,7 @@ ulong max_prepared_stmt_count;
|
||||
statements.
|
||||
*/
|
||||
ulong prepared_stmt_count=0;
|
||||
my_thread_id global_thread_id= 1;
|
||||
my_thread_id global_thread_id= 0;
|
||||
ulong current_pid;
|
||||
ulong slow_launch_threads = 0;
|
||||
uint sync_binlog_period= 0, sync_relaylog_period= 0,
|
||||
@ -766,6 +766,9 @@ mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
|
||||
/* This protects against changes in master_info_index */
|
||||
mysql_mutex_t LOCK_active_mi;
|
||||
|
||||
/* This protects connection id.*/
|
||||
mysql_mutex_t LOCK_thread_id;
|
||||
|
||||
/**
|
||||
The below lock protects access to two global server variables:
|
||||
max_prepared_stmt_count and prepared_stmt_count. These variables
|
||||
@ -933,6 +936,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
|
||||
key_LOCK_thread_count, key_LOCK_thread_cache,
|
||||
key_PARTITION_LOCK_auto_inc;
|
||||
PSI_mutex_key key_RELAYLOG_LOCK_index;
|
||||
PSI_mutex_key key_LOCK_thread_id;
|
||||
PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state,
|
||||
key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry;
|
||||
|
||||
@ -970,6 +974,7 @@ static PSI_mutex_info all_server_mutexes[]=
|
||||
{ &key_hash_filo_lock, "hash_filo::lock", 0},
|
||||
{ &key_LOCK_active_mi, "LOCK_active_mi", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_connection_count, "LOCK_connection_count", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_thread_id, "LOCK_thread_id", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_crypt, "LOCK_crypt", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_delayed_create, "LOCK_delayed_create", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_delayed_insert, "LOCK_delayed_insert", PSI_FLAG_GLOBAL},
|
||||
@ -2048,8 +2053,8 @@ pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
|
||||
extern "C" sig_handler print_signal_warning(int sig)
|
||||
{
|
||||
if (global_system_variables.log_warnings)
|
||||
sql_print_warning("Got signal %d from thread %ld", sig,
|
||||
(ulong) my_thread_id());
|
||||
sql_print_warning("Got signal %d from thread %u", sig,
|
||||
my_thread_id());
|
||||
#ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY
|
||||
my_sigset(sig,print_signal_warning); /* int. thread system calls */
|
||||
#endif
|
||||
@ -2335,6 +2340,7 @@ static void clean_up_mutexes()
|
||||
mysql_mutex_destroy(&LOCK_crypt);
|
||||
mysql_mutex_destroy(&LOCK_user_conn);
|
||||
mysql_mutex_destroy(&LOCK_connection_count);
|
||||
mysql_mutex_destroy(&LOCK_thread_id);
|
||||
mysql_mutex_destroy(&LOCK_stats);
|
||||
mysql_mutex_destroy(&LOCK_global_user_client_stats);
|
||||
mysql_mutex_destroy(&LOCK_global_table_stats);
|
||||
@ -4690,6 +4696,8 @@ static int init_thread_environment()
|
||||
&LOCK_short_uuid_generator, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_connection_count,
|
||||
&LOCK_connection_count, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_thread_id,
|
||||
&LOCK_thread_id, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_stats, &LOCK_stats, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_global_user_client_stats,
|
||||
&LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST);
|
||||
@ -8760,7 +8768,7 @@ static int mysql_init_variables(void)
|
||||
denied_connections= 0;
|
||||
executed_events= 0;
|
||||
global_query_id= 1;
|
||||
global_thread_id= 1;
|
||||
global_thread_id= 0;
|
||||
strnmov(server_version, MYSQL_SERVER_VERSION, sizeof(server_version)-1);
|
||||
threads.empty();
|
||||
thread_cache.empty();
|
||||
@ -10420,3 +10428,96 @@ void init_server_psi_keys(void)
|
||||
}
|
||||
|
||||
#endif /* HAVE_PSI_INTERFACE */
|
||||
|
||||
|
||||
/*
|
||||
Connection ID allocation.
|
||||
|
||||
We need to maintain thread_ids in the 32bit range,
|
||||
because this is how it is passed to the client in the protocol.
|
||||
|
||||
The idea is to maintain a id range, initially set to
|
||||
(0,UINT32_MAX). Whenever new id is needed, we increment the
|
||||
lower limit and return its new value.
|
||||
|
||||
On "overflow", if id can not be generated anymore(i.e lower == upper -1),
|
||||
we recalculate the range boundaries.
|
||||
To do that, we first collect thread ids that are in use, by traversing
|
||||
THD list, and find largest region within (0,UINT32_MAX), that is still free.
|
||||
|
||||
*/
|
||||
|
||||
static my_thread_id thread_id_max= UINT_MAX32;
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
/*
|
||||
Find largest unused thread_id range.
|
||||
|
||||
i.e for every number N within the returned range,
|
||||
there is no existing connection with thread_id equal to N.
|
||||
|
||||
The range is exclusive, lower bound is always >=0 and
|
||||
upper bound <=MAX_UINT32.
|
||||
|
||||
@param[out] low - lower bound for the range
|
||||
@param[out] high - upper bound for the range
|
||||
*/
|
||||
static void recalculate_thread_id_range(my_thread_id *low, my_thread_id *high)
|
||||
{
|
||||
std::vector<my_thread_id> ids;
|
||||
|
||||
// Add sentinels
|
||||
ids.push_back(0);
|
||||
ids.push_back(UINT_MAX32);
|
||||
|
||||
mysql_mutex_lock(&LOCK_thread_count);
|
||||
|
||||
I_List_iterator<THD> it(threads);
|
||||
THD *thd;
|
||||
while ((thd=it++))
|
||||
ids.push_back(thd->thread_id);
|
||||
|
||||
mysql_mutex_unlock(&LOCK_thread_count);
|
||||
|
||||
std::sort(ids.begin(), ids.end());
|
||||
my_thread_id max_gap= 0;
|
||||
for (size_t i= 0; i < ids.size() - 1; i++)
|
||||
{
|
||||
my_thread_id gap= ids[i+1] - ids[i];
|
||||
if (gap > max_gap)
|
||||
{
|
||||
*low= ids[i];
|
||||
*high= ids[i+1];
|
||||
max_gap= gap;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_gap < 2)
|
||||
{
|
||||
/* Can't find free id. This is not really possible,
|
||||
we'd need 2^32 connections for this to happen.*/
|
||||
sql_print_error("Cannot find free connection id.");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my_thread_id next_thread_id(void)
|
||||
{
|
||||
my_thread_id retval;
|
||||
DBUG_EXECUTE_IF("thread_id_overflow", global_thread_id= thread_id_max-2;);
|
||||
|
||||
mysql_mutex_lock(&LOCK_thread_id);
|
||||
|
||||
if (unlikely(global_thread_id == thread_id_max - 1))
|
||||
{
|
||||
recalculate_thread_id_range(&global_thread_id, &thread_id_max);
|
||||
}
|
||||
|
||||
retval= ++global_thread_id;
|
||||
|
||||
mysql_mutex_unlock(&LOCK_thread_id);
|
||||
return retval;
|
||||
}
|
||||
|
10
sql/mysqld.h
10
sql/mysqld.h
@ -722,15 +722,7 @@ inline query_id_t get_query_id()
|
||||
}
|
||||
|
||||
/* increment global_thread_id and return it. */
|
||||
inline __attribute__((warn_unused_result)) my_thread_id next_thread_id()
|
||||
{
|
||||
return my_atomic_add64_explicit((int64*) &global_thread_id, 1, MY_MEMORY_ORDER_RELAXED);
|
||||
}
|
||||
|
||||
#if defined(MYSQL_DYNAMIC_PLUGIN) && defined(_WIN32)
|
||||
extern "C" my_thread_id next_thread_id_noinline();
|
||||
#define next_thread_id() next_thread_id_noinline()
|
||||
#endif
|
||||
extern __attribute__((warn_unused_result)) my_thread_id next_thread_id(void);
|
||||
|
||||
/*
|
||||
TODO: Replace this with an inline function.
|
||||
|
Reference in New Issue
Block a user