-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
MDEV-40298 Use-After-Free when SQL Thread stops in the middle of SHOW SLAVE STATUS #5383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 10.6
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| CHANGE MASTER TO master_host='127.0.0.1', master_user='root'; | ||
| START SLAVE SQL_THREAD; | ||
| include/wait_for_slave_sql_to_start.inc | ||
| SET @@SESSION.debug_sync= | ||
| 'hold_sss_with_run_lock SIGNAL sss_got_run_lock WAIT_FOR sss_continue'; | ||
| SHOW SLAVE STATUS; | ||
| connect stopper, 127.0.0.1, root, , , $SERVER_MYPORT_1; | ||
| SET @@SESSION.debug_sync= 'now WAIT_FOR sss_got_run_lock'; | ||
| STOP SLAVE SQL_THREAD; | ||
| connect continuer, 127.0.0.1, root, , , $SERVER_MYPORT_1; | ||
| SET @@SESSION.debug_sync= 'now SIGNAL sss_continue'; | ||
| connection stopper; | ||
| disconnect continuer; | ||
| connection default; | ||
| disconnect stopper; | ||
| SET @@SESSION.debug_sync= RESET; | ||
| include/wait_for_slave_sql_to_stop.inc | ||
| RESET SLAVE ALL; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # MDEV-40298 | ||
| # Use-After-Free when SQL Thread stops in the middle of SHOW SLAVE STATUS | ||
| # | ||
| # Test that the correct synchronization is performed | ||
|
|
||
| --source include/have_debug_sync.inc | ||
| --source include/have_binlog_format_mixed.inc # No actual replication required | ||
| CHANGE MASTER TO master_host='127.0.0.1', master_user='root'; # basic setup | ||
|
|
||
| START SLAVE SQL_THREAD; | ||
| --source include/wait_for_slave_sql_to_start.inc | ||
|
|
||
|
|
||
| SET @@SESSION.debug_sync= | ||
| 'hold_sss_with_run_lock SIGNAL sss_got_run_lock WAIT_FOR sss_continue'; | ||
| --send SHOW SLAVE STATUS | ||
|
|
||
| --connect (stopper, 127.0.0.1, root, , , $SERVER_MYPORT_1) | ||
| SET @@SESSION.debug_sync= 'now WAIT_FOR sss_got_run_lock'; | ||
| --send STOP SLAVE SQL_THREAD | ||
| # Wait a bit for the thread to "shut down" | ||
| # (It should not shut down, but block waiting for the ongoing SSS; | ||
| # the tested bug instead pulled this carpet under the SSS's feet.) | ||
| --sleep 1 | ||
|
|
||
| --connect (continuer, 127.0.0.1, root, , , $SERVER_MYPORT_1) | ||
| SET @@SESSION.debug_sync= 'now SIGNAL sss_continue'; | ||
| --connection stopper | ||
| --disconnect continuer | ||
|
|
||
| --reap | ||
| --connection default | ||
| --disconnect stopper | ||
|
|
||
|
|
||
| --disable_result_log | ||
| --reap | ||
| --enable_result_log | ||
|
|
||
| # Clean-up | ||
| SET @@SESSION.debug_sync= RESET; | ||
| --source include/wait_for_slave_sql_to_stop.inc | ||
| RESET SLAVE ALL; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3301,10 +3301,13 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, | |
| protocol->store(mi->connection_name.str, mi->connection_name.length, | ||
| &my_charset_bin); | ||
|
|
||
| mysql_mutex_lock(&mi->run_lock); | ||
| mysql_mutex_lock(&mi->rli.run_lock); | ||
| THD *sql_thd= mi->rli.sql_driver_thd; | ||
| DEBUG_SYNC(thd, "hold_sss_with_run_lock"); | ||
| const char *slave_sql_running_state= | ||
| sql_thd ? sql_thd->get_proc_info() : ""; | ||
| mysql_mutex_unlock(&mi->rli.run_lock); | ||
| mysql_mutex_lock(&mi->run_lock); | ||
| THD *io_thd= mi->io_thd; | ||
| const char *slave_io_running_state= io_thd ? io_thd->get_proc_info() : ""; | ||
| mysql_mutex_unlock(&mi->run_lock); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code documentation for
But Is the code comment obsolete, or could there have been a deadlock this whole time?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is about the requirements if we need to hold the run_lock and the data_lock at the same time. Then it is necessary to take them always in the same order to avoid the deadlock where one thread is holding lock A and waiting for B, while another thread is holding B and waiting for A. But when one lock is released before taking the other lock, there is no ordering requirement. You can see some places in the code where we use |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While holding
mi->rli.run_lockandmi->run_lockprotectssql_thdandio_thdfrom being destroyed during the call toget_proc_info(), once the locks are released, the threads can terminate and theirTHDobjects can be destroyed. Ifget_proc_info()returns a dynamically allocated string (e.g., allocated on the thread'smem_root), the pointersslave_sql_running_stateandslave_io_running_statewill become dangling pointers, leading to a Use-After-Free when they are later sent to the client viaprotocol->store.To prevent this, we should copy the state strings into local
Stringbuffers while holding the respective locks.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then this is UB due to violation of contract, because
THD::proc_infois forbidden to return a dynamic string.MDEV-36839 is a prior incident.