From 2a7bc3ba81f8ea3858495e3a0d42938e8ed535ed Mon Sep 17 00:00:00 2001 From: ParadoxV5 Date: Tue, 14 Jul 2026 21:11:28 -0600 Subject: [PATCH] MDEV-40298 Use-After-Free when SQL Thread stops in the middle of SHOW SLAVE STATUS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MDEV-36287 acknowledged that SHOW SLAVE STATUS needed to acquire a mutex lock before accessing the SQL Thread’s THD, as otherwise it may access invalid memory if a concurrent STOP SLAVE deletes the THD. But it missed that the SQL Thread’s mutex is `mi->rli.run_lock`, not `mi->run_lock`, so the bug was still not fixed. This commit fills in the oversight by acquiring the correct corresponding lock for each of `Slave_IO_State` & `Slave_SQL_State`. Reviewed-by: Kristian Nielsen --- mysql-test/main/rli_run_lock.result | 18 ++++++++++++ mysql-test/main/rli_run_lock.test | 43 +++++++++++++++++++++++++++++ sql/slave.cc | 5 +++- 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 mysql-test/main/rli_run_lock.result create mode 100644 mysql-test/main/rli_run_lock.test diff --git a/mysql-test/main/rli_run_lock.result b/mysql-test/main/rli_run_lock.result new file mode 100644 index 0000000000000..e2776a356d0d2 --- /dev/null +++ b/mysql-test/main/rli_run_lock.result @@ -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; diff --git a/mysql-test/main/rli_run_lock.test b/mysql-test/main/rli_run_lock.test new file mode 100644 index 0000000000000..ca8eaf4d92943 --- /dev/null +++ b/mysql-test/main/rli_run_lock.test @@ -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; diff --git a/sql/slave.cc b/sql/slave.cc index 370b2509a1e18..9ca95dee93851 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -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);