Bug#58136: Crash in vio_close at concurrent disconnect and KILL
The problem is a race between a session closing its vio (i.e. after a COM_QUIT) at the same time it is being killed by another thread. This could trigger a assertion in vio_close() as the two threads could end up closing the same vio, at the same time. This could happen due to the implementation of SIGNAL_WITH_VIO_CLOSE, which closes the vio of the thread being killed. The solution is to serialize the close of the Vio under LOCK_thd_data, which protects THD data. No regression test is added as this is essentially a debug issue and the test case would be quite convoluted as we would need to synchronize a session that is being killed -- which is a bit difficult since debug sync points code does not synchronize killed sessions.
This commit is contained in:
parent
eb5a21dd90
commit
487f9bb219
@ -1134,7 +1134,7 @@ static void close_connections(void)
|
|||||||
tmp->thread_id,
|
tmp->thread_id,
|
||||||
(tmp->main_security_ctx.user ?
|
(tmp->main_security_ctx.user ?
|
||||||
tmp->main_security_ctx.user : ""));
|
tmp->main_security_ctx.user : ""));
|
||||||
close_connection(tmp,0,0);
|
close_connection(tmp);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
|
DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
|
||||||
@ -1960,39 +1960,28 @@ static void network_init(void)
|
|||||||
/**
|
/**
|
||||||
Close a connection.
|
Close a connection.
|
||||||
|
|
||||||
@param thd Thread handle
|
@param thd Thread handle.
|
||||||
@param errcode Error code to print to console
|
@param sql_errno The error code to send before disconnect.
|
||||||
@param lock 1 if we have have to lock LOCK_thread_count
|
|
||||||
|
|
||||||
@note
|
@note
|
||||||
For the connection that is doing shutdown, this is called twice
|
For the connection that is doing shutdown, this is called twice
|
||||||
*/
|
*/
|
||||||
void close_connection(THD *thd, uint errcode, bool lock)
|
void close_connection(THD *thd, uint sql_errno)
|
||||||
{
|
{
|
||||||
st_vio *vio;
|
|
||||||
DBUG_ENTER("close_connection");
|
DBUG_ENTER("close_connection");
|
||||||
DBUG_PRINT("enter",("fd: %s error: '%s'",
|
|
||||||
thd->net.vio ? vio_description(thd->net.vio) :
|
if (sql_errno)
|
||||||
"(not connected)",
|
net_send_error(thd, sql_errno, ER_DEFAULT(sql_errno), NULL);
|
||||||
errcode ? ER_DEFAULT(errcode) : ""));
|
|
||||||
if (lock)
|
thd->disconnect();
|
||||||
mysql_mutex_lock(&LOCK_thread_count);
|
|
||||||
thd->killed= THD::KILL_CONNECTION;
|
MYSQL_CONNECTION_DONE((int) sql_errno, thd->thread_id);
|
||||||
if ((vio= thd->net.vio) != 0)
|
|
||||||
{
|
|
||||||
if (errcode)
|
|
||||||
net_send_error(thd, errcode,
|
|
||||||
ER_DEFAULT(errcode), NULL); /* purecov: inspected */
|
|
||||||
vio_close(vio); /* vio is freed in delete thd */
|
|
||||||
}
|
|
||||||
if (lock)
|
|
||||||
mysql_mutex_unlock(&LOCK_thread_count);
|
|
||||||
MYSQL_CONNECTION_DONE((int) errcode, thd->thread_id);
|
|
||||||
if (MYSQL_CONNECTION_DONE_ENABLED())
|
if (MYSQL_CONNECTION_DONE_ENABLED())
|
||||||
{
|
{
|
||||||
sleep(0); /* Workaround to avoid tailcall optimisation */
|
sleep(0); /* Workaround to avoid tailcall optimisation */
|
||||||
}
|
}
|
||||||
MYSQL_AUDIT_NOTIFY_CONNECTION_DISCONNECT(thd, errcode);
|
MYSQL_AUDIT_NOTIFY_CONNECTION_DISCONNECT(thd, sql_errno);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
#endif /* EMBEDDED_LIBRARY */
|
#endif /* EMBEDDED_LIBRARY */
|
||||||
@ -4951,8 +4940,8 @@ void create_thread_to_handle_connection(THD *thd)
|
|||||||
my_snprintf(error_message_buff, sizeof(error_message_buff),
|
my_snprintf(error_message_buff, sizeof(error_message_buff),
|
||||||
ER_THD(thd, ER_CANT_CREATE_THREAD), error);
|
ER_THD(thd, ER_CANT_CREATE_THREAD), error);
|
||||||
net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL);
|
net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL);
|
||||||
|
close_connection(thd);
|
||||||
mysql_mutex_lock(&LOCK_thread_count);
|
mysql_mutex_lock(&LOCK_thread_count);
|
||||||
close_connection(thd,0,0);
|
|
||||||
delete thd;
|
delete thd;
|
||||||
mysql_mutex_unlock(&LOCK_thread_count);
|
mysql_mutex_unlock(&LOCK_thread_count);
|
||||||
return;
|
return;
|
||||||
@ -4993,7 +4982,7 @@ static void create_new_thread(THD *thd)
|
|||||||
mysql_mutex_unlock(&LOCK_connection_count);
|
mysql_mutex_unlock(&LOCK_connection_count);
|
||||||
|
|
||||||
DBUG_PRINT("error",("Too many connections"));
|
DBUG_PRINT("error",("Too many connections"));
|
||||||
close_connection(thd, ER_CON_COUNT_ERROR, 1);
|
close_connection(thd, ER_CON_COUNT_ERROR);
|
||||||
delete thd;
|
delete thd;
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
@ -5374,7 +5363,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
|
|||||||
if (!(thd->net.vio= vio_new_win32pipe(hConnectedPipe)) ||
|
if (!(thd->net.vio= vio_new_win32pipe(hConnectedPipe)) ||
|
||||||
my_net_init(&thd->net, thd->net.vio))
|
my_net_init(&thd->net, thd->net.vio))
|
||||||
{
|
{
|
||||||
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
close_connection(thd, ER_OUT_OF_RESOURCES);
|
||||||
delete thd;
|
delete thd;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -5569,7 +5558,7 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
|
|||||||
event_conn_closed)) ||
|
event_conn_closed)) ||
|
||||||
my_net_init(&thd->net, thd->net.vio))
|
my_net_init(&thd->net, thd->net.vio))
|
||||||
{
|
{
|
||||||
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
close_connection(thd, ER_OUT_OF_RESOURCES);
|
||||||
errmsg= 0;
|
errmsg= 0;
|
||||||
goto errorconn;
|
goto errorconn;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
|
|||||||
some places */
|
some places */
|
||||||
/* Function prototypes */
|
/* Function prototypes */
|
||||||
void kill_mysql(void);
|
void kill_mysql(void);
|
||||||
void close_connection(THD *thd, uint errcode, bool lock);
|
void close_connection(THD *thd, uint sql_errno= 0);
|
||||||
void handle_connection_in_main_thread(THD *thd);
|
void handle_connection_in_main_thread(THD *thd);
|
||||||
void create_thread_to_handle_connection(THD *thd);
|
void create_thread_to_handle_connection(THD *thd);
|
||||||
void unlink_thd(THD *thd);
|
void unlink_thd(THD *thd);
|
||||||
|
@ -1283,6 +1283,40 @@ void THD::awake(THD::killed_state state_to_set)
|
|||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Close the Vio associated this session.
|
||||||
|
|
||||||
|
@remark LOCK_thd_data is taken due to the fact that
|
||||||
|
the Vio might be disassociated concurrently.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void THD::disconnect()
|
||||||
|
{
|
||||||
|
Vio *vio= NULL;
|
||||||
|
|
||||||
|
mysql_mutex_lock(&LOCK_thd_data);
|
||||||
|
|
||||||
|
killed= THD::KILL_CONNECTION;
|
||||||
|
|
||||||
|
#ifdef SIGNAL_WITH_VIO_CLOSE
|
||||||
|
/*
|
||||||
|
Since a active vio might might have not been set yet, in
|
||||||
|
any case save a reference to avoid closing a inexistent
|
||||||
|
one or closing the vio twice if there is a active one.
|
||||||
|
*/
|
||||||
|
vio= active_vio;
|
||||||
|
close_active_vio();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Disconnect even if a active vio is not associated. */
|
||||||
|
if (net.vio != vio)
|
||||||
|
vio_close(net.vio);
|
||||||
|
|
||||||
|
mysql_mutex_unlock(&LOCK_thd_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Remember the location of thread info, the structure needed for
|
Remember the location of thread info, the structure needed for
|
||||||
sql_alloc() and the structure for the net buffer
|
sql_alloc() and the structure for the net buffer
|
||||||
|
@ -2209,6 +2209,9 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
void awake(THD::killed_state state_to_set);
|
void awake(THD::killed_state state_to_set);
|
||||||
|
|
||||||
|
/** Disconnect the associated communication endpoint. */
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
#ifndef MYSQL_CLIENT
|
#ifndef MYSQL_CLIENT
|
||||||
enum enum_binlog_query_type {
|
enum enum_binlog_query_type {
|
||||||
/* The query can be logged in row format or in statement format. */
|
/* The query can be logged in row format or in statement format. */
|
||||||
|
@ -518,7 +518,7 @@ bool setup_connection_thread_globals(THD *thd)
|
|||||||
{
|
{
|
||||||
if (thd->store_globals())
|
if (thd->store_globals())
|
||||||
{
|
{
|
||||||
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
close_connection(thd, ER_OUT_OF_RESOURCES);
|
||||||
statistic_increment(aborted_connects,&LOCK_status);
|
statistic_increment(aborted_connects,&LOCK_status);
|
||||||
MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
|
MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
|
||||||
return 1; // Error
|
return 1; // Error
|
||||||
@ -693,7 +693,7 @@ void do_handle_one_connection(THD *thd_arg)
|
|||||||
|
|
||||||
if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0))
|
if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0))
|
||||||
{
|
{
|
||||||
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
close_connection(thd, ER_OUT_OF_RESOURCES);
|
||||||
statistic_increment(aborted_connects,&LOCK_status);
|
statistic_increment(aborted_connects,&LOCK_status);
|
||||||
MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
|
MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
|
||||||
return;
|
return;
|
||||||
@ -751,7 +751,7 @@ void do_handle_one_connection(THD *thd_arg)
|
|||||||
end_connection(thd);
|
end_connection(thd);
|
||||||
|
|
||||||
end_thread:
|
end_thread:
|
||||||
close_connection(thd, 0, 1);
|
close_connection(thd);
|
||||||
if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))
|
if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))
|
||||||
return; // Probably no-threads
|
return; // Probably no-threads
|
||||||
|
|
||||||
|
@ -600,7 +600,7 @@ void do_handle_bootstrap(THD *thd)
|
|||||||
if (my_thread_init() || thd->store_globals())
|
if (my_thread_init() || thd->store_globals())
|
||||||
{
|
{
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
close_connection(thd, ER_OUT_OF_RESOURCES);
|
||||||
#endif
|
#endif
|
||||||
thd->fatal_error();
|
thd->fatal_error();
|
||||||
goto end;
|
goto end;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user