Skip to content

MDEV-40759: Fix --force-read crash on checksum-failing Format Description event - #5552

Open
prathamesh04 wants to merge 1 commit into
MariaDB:mainfrom
prathamesh04:mdev-40759-force-read-unknown-event-crash
Open

MDEV-40759: Fix --force-read crash on checksum-failing Format Description event#5552
prathamesh04 wants to merge 1 commit into
MariaDB:mainfrom
prathamesh04:mdev-40759-force-read-unknown-event-crash

Conversation

@prathamesh04

Copy link
Copy Markdown

Fixes MDEV-40759.

Problem

With --force-read, Log_event::read_log_event() returns an
Unknown_log_event (not NULL) when a Format Description event's checksum is
invalid. check_header()'s --start-position prelude blindly down-cast such
an event to Format_description_log_event, which is undefined behaviour (a
vptr error under UBSan, and a crash under -D_GLIBCXX_DEBUG), and in
--base64-output modes other than AUTO also silently swallowed the event.

Fix

  • client/mysqlbinlog.cc: in check_header(), report a checksum-failing
    Format Description event through process_event() in both output modes
    (instead of down-casting it), then continue with the default Format
    Description event.
  • sql/log_event.cc: only assert "no result on error" when --force-read is
    not in effect, and free the discarded best-effort Unknown_log_event
    (avoids a memory leak).

Test

binlog_mysqlbinlog_force_read simulates the checksum failure with
--debug=d,simulate_checksum_test_failure and checks both AUTO and
--base64-output=NEVER modes. Verified with the debug MTR run:

binlog.binlog_mysqlbinlog_force_read 'mix' [ pass ]

…tion event

With --force-read, a Format Description event whose checksum is invalid is
returned by Log_event::read_log_event() as an Unknown_log_event instead of
NULL.  check_header()'s prelude blindly down-cast such an event to
Format_description_log_event, which is undefined behaviour (a vptr error
under UBSan) and, in --base64-output modes other than AUTO, also silently
swallowed the event.

Report the checksum-failing event through process_event() in both output
modes and continue with the default Format Description event.  In
Log_event::read_log_event(), only assert 'no result on error' when
--force-read is not in effect, and free the discarded best-effort
Unknown_log_event so it is not leaked.

Add a binlog suite test that simulates the checksum failure with
--debug=d,simulate_checksum_test_failure.
@prathamesh04
prathamesh04 force-pushed the mdev-40759-force-read-unknown-event-crash branch from c648fe8 to 6cf2bc2 Compare August 15, 2026 06:41
Comment thread sql/log_event.cc

@ParadoxV5 ParadoxV5 Aug 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your interest, but we do merge-ups instead of backports here, so please base your PR on 10.11 (where another fix has made this part of your patch obsolete) rather than main.

Comment thread client/mysqlbinlog.cc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach is new to me, but I’m not convinced.

A Format Description Event that’s invalid (whether because of a checksum mismatch or some other corruption) is still meant to describe the log’s format.
Both ignoring it and substituting with “a default” FDE would be very wrong (at least for the premise of this check_header() function, in contrast to the dream where the default FDE is already sufficient).

The only case this would not matter is when two FDEs appear back-to-back – this can only come from a handcrafted log, AFAIK.

Comment on lines +17 to +22
INSERT INTO t1 VALUES (1);

--let $MYSQL_DATADIR= `SELECT @@datadir`
--let $binlog_file= query_get_value(SHOW BINLOG STATUS, File, 1)
--let $binlog_start= query_get_value(SHOW BINLOG STATUS, Position, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, you can smell how wrong it gets from how mariadb-binlog fails if you have some content after start-position:

Suggested change
INSERT INTO t1 VALUES (1);
--let $MYSQL_DATADIR= `SELECT @@datadir`
--let $binlog_file= query_get_value(SHOW BINLOG STATUS, File, 1)
--let $binlog_start= query_get_value(SHOW BINLOG STATUS, Position, 1)
--let $MYSQL_DATADIR= `SELECT @@datadir`
--let $binlog_file= query_get_value(SHOW BINLOG STATUS, File, 1)
--let $binlog_start= query_get_value(SHOW BINLOG STATUS, Position, 1)
INSERT INTO t1 VALUES (1);

In the final test, you can use the DROP below as the “content after start-position” rather than this INSERT.

# and, in --base64-output modes other than AUTO, silently swallowed the event.
#
# The checksum failure is simulated with --debug=d,simulate_checksum_test_failure.
--source include/mysqlbinlog_have_debug.inc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, what’s this? It’s not used in either 10.11 or the base main… 🧐🤔

@ParadoxV5 ParadoxV5 Aug 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It came from before the hard-forking and is never used…

Personal opinion: It’s good that it’s not used, because it works by scanning the entire mariadb-binlog --help
(Unlike have_debug.inc from long ago; though suite.pm still scans the entire mariadbd --help, from which have_debug.inc is now derived.)
There ought to be more efficient ways.

Comment on lines +14 to +16
# Grow the current binlog past the Format Description event so that
# check_header()'s --start-position prelude has to read it.
CREATE TABLE t1 (a INT);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: A modern “blank” binlog is already past the FDE: There’s the GTID List, a Binlog Checkpoint for XA, maybe (forgot if there is) a Rotate.

Manually growing the log is also fine, to not depend on those additional header events, especially since we do want to test with some manually controlled content below.

@ParadoxV5 ParadoxV5 added External Contribution All PRs from entities outside of MariaDB Foundation, Corporation, Codership agreements. Replication Patches involved in replication labels Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

External Contribution All PRs from entities outside of MariaDB Foundation, Corporation, Codership agreements. Replication Patches involved in replication

Development

Successfully merging this pull request may close these issues.

2 participants