diff --git a/mysql-test/suite/binlog/r/fdle_overflow.result b/mysql-test/suite/binlog/r/fdle_overflow.result index cb2e2f293ccb0..3fde805de82c2 100644 --- a/mysql-test/suite/binlog/r/fdle_overflow.result +++ b/mysql-test/suite/binlog/r/fdle_overflow.result @@ -1,11 +1,10 @@ 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; -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..1163e745ed040 100644 --- a/mysql-test/suite/binlog/t/fdle_overflow.test +++ b/mysql-test/suite/binlog/t/fdle_overflow.test @@ -11,12 +11,15 @@ 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) +--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 +28,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/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..579d7b4c420ab --- /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 ec36fc5692682..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) @@ -2416,11 +2417,12 @@ 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_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) checksum_alg= BINLOG_CHECKSUM_ALG_OFF; return ret; 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);