From 57314f22e57f884828d67c8e5bb5da6cb5c4023f Mon Sep 17 00:00:00 2001 From: ParadoxV5 Date: Thu, 6 Aug 2026 19:17:41 -0600 Subject: [PATCH 1/3] MDEV-40365/MDEV-40366 test fixes * The crafted invalid FDEs were omitted their checksums, even though the base code (still) adds the checksum length to the event length. This commit fixes this discrepancy by not skipping the event footer step (write the checksum, and finish up encryption if active), so the fault injections are more self-contained. This discrepancy did not matter in practice because * The event loading simply assumes the first few bytes of the next event as the unused checksum. * The fix to `get_checksum_alg()` is detecting invalidity before the code reaches the fixed parser-contructor. This is rather an implementation detail, though, as the constructor fix would come to effect if we refactor `get_checksum_alg()` away. * This commit also disables echoing `SHOW BINLOG EVENTS IN` to the results in case the `$binlog_file` is not consistent. --- mysql-test/suite/binlog/r/fdle_overflow.result | 2 -- mysql-test/suite/binlog/t/fdle_overflow.test | 4 ++++ sql/log_event_server.cc | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mysql-test/suite/binlog/r/fdle_overflow.result b/mysql-test/suite/binlog/r/fdle_overflow.result index cb2e2f293ccb0..d88d0305ca78e 100644 --- a/mysql-test/suite/binlog/r/fdle_overflow.result +++ b/mysql-test/suite/binlog/r/fdle_overflow.result @@ -1,11 +1,9 @@ SET @saved_dbug= @@GLOBAL.debug_dbug; SET @@GLOBAL.debug_dbug= '+d,truncate_fde_at_post_header_len'; FLUSH BINARY LOGS; -SHOW BINLOG EVENTS IN 'master-bin.000002'; ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Wrong offset or I/O error SET @@GLOBAL.debug_dbug= '+d,truncate_fde_common_header_len'; FLUSH BINARY LOGS; -SHOW BINLOG EVENTS IN 'master-bin.000003'; ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Wrong offset or I/O error SET @@GLOBAL.debug_dbug= @saved_dbug; RESET MASTER; diff --git a/mysql-test/suite/binlog/t/fdle_overflow.test b/mysql-test/suite/binlog/t/fdle_overflow.test index b4a997abe1a88..cc2374e9f6923 100644 --- a/mysql-test/suite/binlog/t/fdle_overflow.test +++ b/mysql-test/suite/binlog/t/fdle_overflow.test @@ -15,8 +15,10 @@ SET @@GLOBAL.debug_dbug= '+d,truncate_fde_at_post_header_len'; FLUSH BINARY LOGS; --let $binlog_file= query_get_value(SHOW BINLOG STATUS, File, 1) +--disable_query_log --error ER_ERROR_WHEN_EXECUTING_COMMAND --eval SHOW BINLOG EVENTS IN '$binlog_file' +--enable_query_log # MDEV-40365: `common_header_len` & `post_header_len` @@ -25,8 +27,10 @@ SET @@GLOBAL.debug_dbug= '+d,truncate_fde_common_header_len'; FLUSH BINARY LOGS; --let $binlog_file= query_get_value(SHOW BINLOG STATUS, File, 1) +--disable_query_log --error ER_ERROR_WHEN_EXECUTING_COMMAND --eval SHOW BINLOG EVENTS IN '$binlog_file' +--enable_query_log # Clean-up SET @@GLOBAL.debug_dbug= @saved_dbug; diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index ec36fc5692682..7a688acd2114e 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -2416,11 +2416,11 @@ bool Format_description_log_event::write() checksum_alg= BINLOG_CHECKSUM_ALG_CRC32; // Forcing (V) room to fill anyway } ret= write_header(rec_size) || - write_data(buff, buff_size) || - DBUG_EVALUATE_IF("truncate_fde_at_post_header_len", false, - write_data(post_header_len, number_of_event_types) || - write_data(&checksum_byte, sizeof(checksum_byte)) || - write_footer()); + write_data(buff, buff_size) || + DBUG_EVALUATE_IF("truncate_fde_at_post_header_len", false, + write_data(post_header_len, number_of_event_types) || + write_data(&checksum_byte, sizeof(checksum_byte))) || + write_footer(); if (no_checksum) checksum_alg= BINLOG_CHECKSUM_ALG_OFF; return ret; From 190f5b7dd743054207e3b6e47d530f668f03138f Mon Sep 17 00:00:00 2001 From: ParadoxV5 Date: Thu, 6 Aug 2026 20:46:58 -0600 Subject: [PATCH 2/3] [temp] Test MDEV-40647 It does not typically fail, but should trip MSAN. --- .../suite/binlog/r/fdle_overflow.result | 3 +- mysql-test/suite/binlog/t/fdle_overflow.test | 3 +- .../suite/rpl/r/rpl_rotate_ev_overflow.result | 19 +++++++ .../suite/rpl/t/rpl_rotate_ev_overflow.test | 51 +++++++++++++++++++ sql/log_event_server.cc | 10 ++-- 5 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_rotate_ev_overflow.result create mode 100644 mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test diff --git a/mysql-test/suite/binlog/r/fdle_overflow.result b/mysql-test/suite/binlog/r/fdle_overflow.result index d88d0305ca78e..3fde805de82c2 100644 --- a/mysql-test/suite/binlog/r/fdle_overflow.result +++ b/mysql-test/suite/binlog/r/fdle_overflow.result @@ -1,5 +1,6 @@ SET @saved_dbug= @@GLOBAL.debug_dbug; -SET @@GLOBAL.debug_dbug= '+d,truncate_fde_at_post_header_len'; +SET @@GLOBAL.debug_dbug= +'+d,truncate_fde_post_header_len,truncate_fde_used_checksum_alg'; FLUSH BINARY LOGS; ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Wrong offset or I/O error SET @@GLOBAL.debug_dbug= '+d,truncate_fde_common_header_len'; diff --git a/mysql-test/suite/binlog/t/fdle_overflow.test b/mysql-test/suite/binlog/t/fdle_overflow.test index cc2374e9f6923..1163e745ed040 100644 --- a/mysql-test/suite/binlog/t/fdle_overflow.test +++ b/mysql-test/suite/binlog/t/fdle_overflow.test @@ -11,7 +11,8 @@ SET @saved_dbug= @@GLOBAL.debug_dbug; # MDEV-40366: `used_checksum_alg` -SET @@GLOBAL.debug_dbug= '+d,truncate_fde_at_post_header_len'; +SET @@GLOBAL.debug_dbug= + '+d,truncate_fde_post_header_len,truncate_fde_used_checksum_alg'; FLUSH BINARY LOGS; --let $binlog_file= query_get_value(SHOW BINLOG STATUS, File, 1) diff --git a/mysql-test/suite/rpl/r/rpl_rotate_ev_overflow.result b/mysql-test/suite/rpl/r/rpl_rotate_ev_overflow.result new file mode 100644 index 0000000000000..7c21805495019 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_rotate_ev_overflow.result @@ -0,0 +1,19 @@ +SET @saved_dbug= @@GLOBAL.debug_dbug; +SET @@GLOBAL.debug_dbug= '+d,truncate_fde_post_header_len'; +include/master-slave.inc +[connection master] +FLUSH BINARY LOGS; +CALL mtr.add_suppression('Found invalid event in binary log'); +ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Wrong offset or I/O error +connection slave; +START SLAVE IO_THREAD; +CALL mtr.add_suppression('Slave I/O: Relay log write failure'); +include/wait_for_slave_io_error.inc [errno=1595] +connection master; +SET @@GLOBAL.debug_dbug= @saved_dbug; +RESET MASTER; +connection slave; +CHANGE MASTER TO master_use_gtid=SLAVE_POS; +SET @@GLOBAL.gtid_slave_pos=''; +include/start_slave.inc +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test b/mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test new file mode 100644 index 0000000000000..2eac4f7890d16 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test @@ -0,0 +1,51 @@ +# MDEV-40647: OOB read in IO Thread if the FDEv does not support Rotate Events + +--source include/have_debug.inc +--source include/have_binlog_format_mixed.inc # should be format-agnostic + +SET @saved_dbug= @@GLOBAL.debug_dbug; +SET @@GLOBAL.debug_dbug= '+d,truncate_fde_post_header_len'; +--let $rpl_skip_start_slave= 1 +# This setup will also RESET MASTER ... +--source include/master-slave.inc +# ... which means the binlog file is consistent. +--let $binlog_file= master-bin.000001 + +# Use a specific binlog position to avoid the +# test failing early from other metadata events +--let $binlog_start= query_get_value(SHOW BINLOG STATUS, Position, 1) +# Generate the Rotate Event +FLUSH BINARY LOGS; + + +# Control: SHOW BINLOG EVENTS fails as expected. +CALL mtr.add_suppression('Found invalid event in binary log'); +--disable_query_log + --error ER_ERROR_WHEN_EXECUTING_COMMAND + --eval SHOW BINLOG EVENTS IN '$binlog_file' FROM $binlog_start +--enable_query_log + +--connection slave +--disable_query_log + eval CHANGE MASTER TO master_use_gtid=NO, + master_log_file='$binlog_file', master_log_pos=$binlog_start; +--enable_query_log +START SLAVE IO_THREAD; +CALL mtr.add_suppression('Slave I/O: Relay log write failure'); +# ER_SLAVE_RELAY_LOG_WRITE_FAILURE +--let $slave_io_errno= 1595 +# Experiment: The IO thread should fail. +--source include/wait_for_slave_io_error.inc + +# Clean-up +--connection master +SET @@GLOBAL.debug_dbug= @saved_dbug; +RESET MASTER; + +--connection slave +CHANGE MASTER TO master_use_gtid=SLAVE_POS; # restore the default +--disable_warnings + SET @@GLOBAL.gtid_slave_pos=''; # for good measure +--enable_warnings +--source include/start_slave.inc +--source include/rpl_end.inc diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index 7a688acd2114e..b005d34e2866c 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -2376,8 +2376,9 @@ bool Format_description_log_event::write() const size_t buff_size= DBUG_EVALUATE_IF("truncate_fde_common_header_len", ST_COMMON_HEADER_LEN_OFFSET, sizeof(buff)); size_t rec_size= buff_size + - DBUG_EVALUATE_IF("truncate_fde_at_post_header_len", 0, - BINLOG_CHECKSUM_ALG_DESC_LEN + number_of_event_types); + DBUG_EVALUATE_IF("truncate_fde_post_header_len", 0, number_of_event_types) + + DBUG_EVALUATE_IF("truncate_fde_used_checksum_alg", 0, + BINLOG_CHECKSUM_ALG_DESC_LEN); int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version); memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN); if (!dont_set_created) @@ -2417,8 +2418,9 @@ bool Format_description_log_event::write() } ret= write_header(rec_size) || write_data(buff, buff_size) || - DBUG_EVALUATE_IF("truncate_fde_at_post_header_len", false, - write_data(post_header_len, number_of_event_types) || + DBUG_EVALUATE_IF("truncate_fde_post_header_len", false, + write_data(post_header_len, number_of_event_types)) || + DBUG_EVALUATE_IF("truncate_fde_used_checksum_alg", false, write_data(&checksum_byte, sizeof(checksum_byte))) || write_footer(); if (no_checksum) From e139ad6759e48469fc92d149356c47969e7ec263 Mon Sep 17 00:00:00 2001 From: ParadoxV5 Date: Thu, 6 Aug 2026 21:58:45 -0600 Subject: [PATCH 3/3] MDEV-40647 OOB read in IO Thread if the FDEv does not support Rotate Events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the replication IO Thread receives a Rotate event following a Format Description event (FDE) with no post-header length for Rotate events, the Rotate event’s parser constructor indexes the FDE’s post-header lengths array out of bounds. This commit defends against this situation by checking before the constructor that the FDE describes Rotate events as recognized at all. In practice, because the Binlog Dump thread generates a Fake `ROTATE_EVENT` **before** sending the FDE, it has pinned Rotate events’ post-header length to 8 regardless of FDEs. This fix solution considers that the FDE’s description should still be respected, matching the constructor. Reviewed-by: Kristian Nielsen --- mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test | 2 +- sql/slave.cc | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test b/mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test index 2eac4f7890d16..579d7b4c420ab 100644 --- a/mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test +++ b/mysql-test/suite/rpl/t/rpl_rotate_ev_overflow.test @@ -1,4 +1,4 @@ -# MDEV-40647: OOB read in IO Thread if the FDEv does not support Rotate Events +# MDEV-40647 OOB read in IO Thread if the FDEv does not support Rotate Events --source include/have_debug.inc --source include/have_binlog_format_mixed.inc # should be format-agnostic diff --git a/sql/slave.cc b/sql/slave.cc index bbffb3db216e1..5618616b9872f 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -6610,6 +6610,17 @@ static int queue_event(Master_info* mi, const uchar *buf, ulong event_len) goto err; case ROTATE_EVENT: { + /* + It's normally done in Log_event::read_log_event(), + but we bypass it here because it's expensive and costs dynamic memory. + */ + if (unlikely(ROTATE_EVENT > + mi->rli.relay_log.description_event_for_queue->number_of_event_types)) + { + // The current FDE does not support `ROTATE_EVENT`. + error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; + goto err; + } Rotate_log_event rev(buf, checksum_alg != BINLOG_CHECKSUM_ALG_OFF ? event_len - BINLOG_CHECKSUM_LEN : event_len, mi->rli.relay_log.description_event_for_queue);