1
0
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:
Vladislav Vaintroub
2018-02-01 09:01:15 +00:00
parent b56f9fbe2f
commit 313247db9c
4 changed files with 164 additions and 13 deletions

View 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;

View 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;

View File

@ -559,7 +559,7 @@ ulong max_prepared_stmt_count;
statements. statements.
*/ */
ulong prepared_stmt_count=0; ulong prepared_stmt_count=0;
my_thread_id global_thread_id= 1; my_thread_id global_thread_id= 0;
ulong current_pid; ulong current_pid;
ulong slow_launch_threads = 0; ulong slow_launch_threads = 0;
uint sync_binlog_period= 0, sync_relaylog_period= 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 */ /* This protects against changes in master_info_index */
mysql_mutex_t LOCK_active_mi; 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: The below lock protects access to two global server variables:
max_prepared_stmt_count and prepared_stmt_count. These 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_LOCK_thread_count, key_LOCK_thread_cache,
key_PARTITION_LOCK_auto_inc; key_PARTITION_LOCK_auto_inc;
PSI_mutex_key key_RELAYLOG_LOCK_index; 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, PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state,
key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry; 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_hash_filo_lock, "hash_filo::lock", 0},
{ &key_LOCK_active_mi, "LOCK_active_mi", PSI_FLAG_GLOBAL}, { &key_LOCK_active_mi, "LOCK_active_mi", PSI_FLAG_GLOBAL},
{ &key_LOCK_connection_count, "LOCK_connection_count", 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_crypt, "LOCK_crypt", PSI_FLAG_GLOBAL},
{ &key_LOCK_delayed_create, "LOCK_delayed_create", PSI_FLAG_GLOBAL}, { &key_LOCK_delayed_create, "LOCK_delayed_create", PSI_FLAG_GLOBAL},
{ &key_LOCK_delayed_insert, "LOCK_delayed_insert", 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) extern "C" sig_handler print_signal_warning(int sig)
{ {
if (global_system_variables.log_warnings) if (global_system_variables.log_warnings)
sql_print_warning("Got signal %d from thread %ld", sig, sql_print_warning("Got signal %d from thread %u", sig,
(ulong) my_thread_id()); my_thread_id());
#ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY #ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY
my_sigset(sig,print_signal_warning); /* int. thread system calls */ my_sigset(sig,print_signal_warning); /* int. thread system calls */
#endif #endif
@ -2335,6 +2340,7 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_crypt); mysql_mutex_destroy(&LOCK_crypt);
mysql_mutex_destroy(&LOCK_user_conn); mysql_mutex_destroy(&LOCK_user_conn);
mysql_mutex_destroy(&LOCK_connection_count); mysql_mutex_destroy(&LOCK_connection_count);
mysql_mutex_destroy(&LOCK_thread_id);
mysql_mutex_destroy(&LOCK_stats); mysql_mutex_destroy(&LOCK_stats);
mysql_mutex_destroy(&LOCK_global_user_client_stats); mysql_mutex_destroy(&LOCK_global_user_client_stats);
mysql_mutex_destroy(&LOCK_global_table_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); &LOCK_short_uuid_generator, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_connection_count, mysql_mutex_init(key_LOCK_connection_count,
&LOCK_connection_count, MY_MUTEX_INIT_FAST); &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_stats, &LOCK_stats, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_global_user_client_stats, mysql_mutex_init(key_LOCK_global_user_client_stats,
&LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST); &LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST);
@ -8760,7 +8768,7 @@ static int mysql_init_variables(void)
denied_connections= 0; denied_connections= 0;
executed_events= 0; executed_events= 0;
global_query_id= 1; global_query_id= 1;
global_thread_id= 1; global_thread_id= 0;
strnmov(server_version, MYSQL_SERVER_VERSION, sizeof(server_version)-1); strnmov(server_version, MYSQL_SERVER_VERSION, sizeof(server_version)-1);
threads.empty(); threads.empty();
thread_cache.empty(); thread_cache.empty();
@ -10420,3 +10428,96 @@ void init_server_psi_keys(void)
} }
#endif /* HAVE_PSI_INTERFACE */ #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;
}

View File

@ -722,15 +722,7 @@ inline query_id_t get_query_id()
} }
/* increment global_thread_id and return it. */ /* increment global_thread_id and return it. */
inline __attribute__((warn_unused_result)) my_thread_id next_thread_id() extern __attribute__((warn_unused_result)) my_thread_id next_thread_id(void);
{
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
/* /*
TODO: Replace this with an inline function. TODO: Replace this with an inline function.