Commit 48a74d74 authored by Dmitry Shulga's avatar Dmitry Shulga

Fixed bug #51855. Race condition in XA START. If several threads

concurrently execute the statement XA START 'x', then mysqld
server could crash.
parent 8a2f3f4b
...@@ -3365,9 +3365,13 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state) ...@@ -3365,9 +3365,13 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state)
bool xid_cache_insert(XID_STATE *xid_state) bool xid_cache_insert(XID_STATE *xid_state)
{ {
pthread_mutex_lock(&LOCK_xid_cache); pthread_mutex_lock(&LOCK_xid_cache);
DBUG_ASSERT(hash_search(&xid_cache, xid_state->xid.key(), if (hash_search(&xid_cache, xid_state->xid.key(), xid_state->xid.key_length()))
xid_state->xid.key_length())==0); {
my_bool res=my_hash_insert(&xid_cache, (uchar*)xid_state); pthread_mutex_unlock(&LOCK_xid_cache);
my_error(ER_XAER_DUPID, MYF(0));
return TRUE;
}
my_bool res= my_hash_insert(&xid_cache, (uchar*)xid_state);
pthread_mutex_unlock(&LOCK_xid_cache); pthread_mutex_unlock(&LOCK_xid_cache);
return res; return res;
} }
......
...@@ -4730,7 +4730,7 @@ mysql_execute_command(THD *thd) ...@@ -4730,7 +4730,7 @@ mysql_execute_command(THD *thd)
my_error(ER_XAER_NOTA, MYF(0)); my_error(ER_XAER_NOTA, MYF(0));
break; break;
} }
thd->transaction.xid_state.xa_state=XA_ACTIVE; thd->transaction.xid_state.xa_state= XA_ACTIVE;
my_ok(thd); my_ok(thd);
break; break;
} }
...@@ -4750,16 +4750,16 @@ mysql_execute_command(THD *thd) ...@@ -4750,16 +4750,16 @@ mysql_execute_command(THD *thd)
my_error(ER_XAER_OUTSIDE, MYF(0)); my_error(ER_XAER_OUTSIDE, MYF(0));
break; break;
} }
if (xid_cache_search(thd->lex->xid))
{
my_error(ER_XAER_DUPID, MYF(0));
break;
}
DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
thd->transaction.xid_state.xa_state=XA_ACTIVE; thd->transaction.xid_state.xa_state= XA_ACTIVE;
thd->transaction.xid_state.rm_error= 0; thd->transaction.xid_state.rm_error= 0;
thd->transaction.xid_state.xid.set(thd->lex->xid); thd->transaction.xid_state.xid.set(thd->lex->xid);
xid_cache_insert(&thd->transaction.xid_state); if (xid_cache_insert(&thd->transaction.xid_state))
{
thd->transaction.xid_state.xa_state= XA_NOTR;
thd->transaction.xid_state.xid.null();
break;
}
thd->transaction.all.modified_non_trans_table= FALSE; thd->transaction.all.modified_non_trans_table= FALSE;
thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN); thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN);
thd->server_status|= SERVER_STATUS_IN_TRANS; thd->server_status|= SERVER_STATUS_IN_TRANS;
...@@ -4813,6 +4813,16 @@ mysql_execute_command(THD *thd) ...@@ -4813,6 +4813,16 @@ mysql_execute_command(THD *thd)
case SQLCOM_XA_COMMIT: case SQLCOM_XA_COMMIT:
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
{ {
/*
xid_state.in_thd is always true beside of xa recovery
procedure. Note, that there is no race condition here
between xid_cache_search and xid_cache_delete, since we're always
deleting our own XID (thd->lex->xid == thd->transaction.xid_state.xid).
The only case when thd->lex->xid != thd->transaction.xid_state.xid
and xid_state->in_thd == 0 is in ha_recover() functionality,
which is called before starting client connections, and thus is
always single-threaded.
*/
XID_STATE *xs=xid_cache_search(thd->lex->xid); XID_STATE *xs=xid_cache_search(thd->lex->xid);
if (!xs || xs->in_thd) if (!xs || xs->in_thd)
my_error(ER_XAER_NOTA, MYF(0)); my_error(ER_XAER_NOTA, MYF(0));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment