MDEV-40646: Undersized Table_map Metadata can Crash Slave - #5524
Open
bnestere wants to merge 2 commits into
Open
MDEV-40646: Undersized Table_map Metadata can Crash Slave#5524bnestere wants to merge 2 commits into
bnestere wants to merge 2 commits into
Conversation
A slave can crash or leak heap content when its master sends a Table_map event that declares columns whose types need more field metadata than the event carries. Building its description of the table, the slave reads past the end of the field metadata the event holds. The bytes beyond the buffer are decoded as column metadata. A Table_map event carries a column count, one type byte per column, and a block of field metadata. A column's type fixes how many metadata bytes it consumes: two for the string, enum, set, bit, varchar, and decimal types, one for the blob, float, and high precision temporal types, none for the rest. The master writes exactly that many bytes per column, so the metadata block a well formed event carries is the sum over its columns. The slave sizes the metadata block from the event and, while building its table_def, walks the columns in order and reads each type's metadata bytes from that block. The slave checked only that the metadata block was no larger than two bytes per column. It never checked the block against the column types themselves. table_def() then walked every declared column and indexed the metadata block for each, so a column whose type needed metadata the block did not hold read past the end of it. A column count large enough carried that read far past the allocation. The Table_map reader now sums the metadata the declared column types consume and rejects the event when the block is smaller than that sum, so is_valid() reports the event as invalid and the SQL thread stops with a relay log read failure before any column is decoded. A block larger than the sum is still accepted, because every consumer reads only the bytes the types call for. A column type this slave does not recognize counts as zero in the sum, so Table_map events from a newer master using such a type still parse. A Table_map whose metadata cannot cover its column types now stops the slave with an error instead of reading past the buffer. Note that the regression test does not crash a regular debug build; an ASAN build is required to show the invalid memory access. The 24 column table map it injects overruns the metadata allocation by a bounded amount that lands in mapped heap, which a debug build reads without faulting and then stops the SQL thread with ER_SLAVE_CONVERSION_FAILED on the column type mismatch. Reviewed-by: TODO Signed-off-by: Brandon Nesterenko <brandon.nesterenko@mariadb.com>
ParadoxV5
reviewed
Aug 11, 2026
Contributor
There was a problem hiding this comment.
If the bug is here, then is actual replication required to reproduce, or is a faulty binlog + SHOW BINLOG EVENTS (or mariadb-binlog) sufficient?
Member
|
Brandon Nesterenko ***@***.***> writes:
A slave can crash or leak heap content when its master sends a Table_map
event that declares columns whose types need more field metadata than
the event carries. Building its description of the table, the slave
reads past the end of the field metadata the event holds. The bytes
beyond the buffer are decoded as column metadata.
This one looks fine to me, too.
Reviewed-by: Kristian Nielsen ***@***.***>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
** This will actually go into 10.11, but the work I did was on 10.6, so let's do the review on 10.6 and I'll just push it to 10.11**
A slave can crash or leak heap content when its master sends a Table_map
event that declares columns whose types need more field metadata than
the event carries. Building its description of the table, the slave
reads past the end of the field metadata the event holds. The bytes
beyond the buffer are decoded as column metadata.
A Table_map event carries a column count, one type byte per column,
and a block of field metadata. A column's type fixes how many metadata
bytes it consumes: two for the string, enum, set, bit, varchar, and
decimal types, one for the blob, float, and high precision temporal
types, none for the rest. The master writes exactly that many bytes
per column, so the metadata block a well formed event carries is the
sum over its columns. The slave sizes the metadata block from the
event and, while building its table_def, walks the columns in order
and reads each type's metadata bytes from that block.
The slave checked only that the metadata block was no larger than two
bytes per column. It never checked the block against the column types
themselves. table_def() then walked every declared column and indexed
the metadata block for each, so a column whose type needed metadata
the block did not hold read past the end of it. A column count large
enough carried that read far past the allocation.
The Table_map reader now sums the metadata the declared column types
consume and rejects the event when the block is smaller than that
sum, so is_valid() reports the event as invalid and the SQL thread
stops with a relay log read failure before any column is decoded. A
block larger than the sum is still accepted, because every consumer
reads only the bytes the types call for. A column type this slave
does not recognize counts as zero in the sum, so Table_map events
from a newer master using such a type still parse. A Table_map whose
metadata cannot cover its column types now stops the slave with an
error instead of reading past the buffer.
Note that the regression test does not crash a regular debug build;
an ASAN build is required to show the invalid memory access. The 24
column table map it injects overruns the metadata allocation by a
bounded amount that lands in mapped heap, which a debug build reads
without faulting and then stops the SQL thread with
ER_SLAVE_CONVERSION_FAILED on the column type mismatch.
This PR is organized as follows. The first commit shows the regression, and
the second commit contains the fix.