Skip to content

MDEV-39689: Fix OOB reads in Table_map_log_event#5270

Open
LukeYL026 wants to merge 1 commit into
MariaDB:10.6from
LukeYL026:mdev-39689-table-map-log-event-slave-overflow
Open

MDEV-39689: Fix OOB reads in Table_map_log_event#5270
LukeYL026 wants to merge 1 commit into
MariaDB:10.6from
LukeYL026:mdev-39689-table-map-log-event-slave-overflow

Conversation

@LukeYL026

@LukeYL026 LukeYL026 commented Jun 23, 2026

Copy link
Copy Markdown

Description

In MariaDB, the Table_map_log_event optional metadata parse_* functions and constructor in sql/log_event.cc call net_field_length() to decode length-encoded integers from the event buffer without validating that the decoded length or advanced pointer remains within bounds. If a corrupted or malicious binlog event contains an inflated length field, net_field_length() advances the read pointer past the buffer end, and subsequent reads (e.g. strmake_root, memcpy, struct assignments) perform OOB memory access.

This can result in:

  • Segfaults when the OOB read hits an unmapped page
  • Data leakage when heap contents are copied into column names, error messages, or struct fields
  • Invalid replica behavior from garbled metadata causing table structure mismatches

parse_* functions

This patch adds if (unlikely(p > end)) return; bounds checks after every net_field_length() call in all affected parse functions. Checks are placed before the decoded value is stored to any struct field, ensuring no OOB data leaks into metadata. On early return, the slave gracefully falls back to positional column mapping — no crash, no OOB read.

Table_map_log_event constructor

Adds if (unlikely(ptr + size > buf + event_len)) bounds checks before each memcpy in the constructor for m_colcnt, m_field_metadata_size, and m_null_bits. On detecting an overflow, the constructor frees allocated memory, sets m_memory = NULL (causing is_valid() to return false), and returns early. The slave then rejects the event gracefully with error 1594.

Affected call sites

  • parse_default_charset
  • parse_enum_and_set_default_charset
  • parse_column_charset
  • parse_enum_and_set_column_charset
  • parse_column_name
  • parse_set_str_value
  • parse_geometry_type
  • parse_simple_pk
  • parse_pk_with_prefix
  • Table_map_log_event constructor (m_colcnt, m_field_metadata, m_null_bits)

Release Notes

Slave no longer crashes or reads out-of-bounds memory when receiving a Table_map_log_event with corrupted optional metadata lengths or corrupted column count/metadata size. The slave either falls back to positional column mapping or rejects the event gracefully.

How can this PR be tested?

New MTR test rpl.rpl_table_map_log_event_overflow covers 9 test cases:

Tests 1-7 (master-side injection, parse_ functions):*

  1. parse_column_name — corrupted column name length
  2. parse_default_charset — corrupted default charset column index
  3. parse_column_charset — corrupted column charset value
  4. parse_set_str_value — corrupted SET string value count
  5. parse_geometry_type — corrupted geometry type value
  6. parse_simple_pk — corrupted simple primary key column index
  7. parse_pk_with_prefix — corrupted prefix primary key column index

Each verifies the slave replicates successfully via positional fallback (no crash, correct data).

Tests 8-9 (slave-side injection, constructor):
8. corrupt_table_map_colcnt_read — inflates decoded m_colcnt on the slave
9. corrupt_table_map_field_metadata_size_read — inflates decoded m_field_metadata_size on the slave

Both verify the slave rejects the event with error 1594 (no crash, no OOB read).

Note: A similar bounds check could be added for infoLen in the Rows_log_event constructor (RW_V_EXTRAINFO_TAG block), but MariaDB never writes extra row info — it only reads it from MySQL 5.6+ masters. Since the code path is unreachable in normal MariaDB replication, it cannot be tested via MTR without extreme levels of wizardry - we recommend code review for QA, instead of expensive-to-maintain test case.

Basing the PR against the correct MariaDB version

This fix targets the oldest LTS version 10.6 and later.
For convenience, a branch for latest preview 13.1 is available on fork, for simplicity when merging change to later versions.

Copyright

All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc.

@CLAassistant

CLAassistant commented Jun 23, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request addresses MDEV-39689 by adding robust bounds checks and validation of optional metadata lengths within Table_map_log_event and its associated parse_* functions, preventing slave overflows on malformed events. The review feedback highlights a critical ordering issue in parse_simple_pk where a bounds check is executed after the decoded index is already used to modify metadata, along with redundant nested braces. Additionally, the feedback points out that if bounds checks fail during Table_map_log_event construction, m_meta_memory is left allocated while m_memory is freed, and recommends explicitly freeing m_meta_memory to prevent a partially-freed state.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread sql/log_event.cc
Comment thread sql/log_event.cc
Comment thread sql/log_event.cc
@LukeYL026 LukeYL026 force-pushed the mdev-39689-table-map-log-event-slave-overflow branch 2 times, most recently from cb10bed to 0562b5c Compare June 23, 2026 23:00
@gkodinov gkodinov added the External Contribution All PRs from entities outside of MariaDB Foundation, Corporation, Codership agreements. label Jun 24, 2026

@gkodinov gkodinov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for your contribution! This is a preliminary review.

First of all: please rebase on the lowest affected version. This is hardening of the log parser(s), so a bug fix as such. I'd do 10.6, if affected.

Then there's a odd looking variable in your diff that probably needs removing.
Otherwise it's good to go IMHO.

Comment thread sql/log_event_server.cc Outdated
uchar const dbuf[]= { (uchar) m_dblen };
uchar const tbuf[]= { (uchar) m_tbllen };

ulong colcnt_to_write= (ulong) m_colcnt;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

any specific reason why you need to convert a ulong value to ulong?
To tell you frankly I do not see a lot of point from colcnt_to_write to begin with.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sorry, this is indeed pointless code that should've been cleaned up earlier.

It used to be as follows when trying to debug using write-side injection:

ulong colcnt_to_write= DBUG_IF("corrupt_table_map_colcnt") ?
                            (1 << 20) : (ulong) m_colcnt;

I since swapped it to read-side injection for debug, but forgot that this variable becomes totally unnecessary after removing DBUG_IF.

Please disregard this line, we have no need for colcnt_to_write now, and I'll clean it up.

@LukeYL026 LukeYL026 force-pushed the mdev-39689-table-map-log-event-slave-overflow branch from 0562b5c to bdc5681 Compare June 24, 2026 22:12
Add bounds checks to prevent out-of-bounds memory reads when parsing
Table_map_log_event optional metadata and constructor fields.

parse_* functions: Add if (unlikely(p > end)) return after every
net_field_length() call. Ensures decoded values are validated before
being stored to struct fields, preventing information disclosure from
OOB reads. On early return, the slave gracefully falls back to
positional column mapping.

Table_map_log_event constructor: Add bounds checks before memcpy calls
for m_colcnt, m_field_metadata_size, and m_null_bits. On overflow, frees
allocated memory and sets m_memory=NULL causing is_valid() to return
false. Adds read-side DBUG_EXECUTE_IF injection points for testing.

Functions fixed:
- parse_default_charset
- parse_column_charset
- parse_column_name
- parse_set_str_value
- parse_geometry_type
- parse_simple_pk
- parse_pk_with_prefix
- Table_map_log_event constructor (m_colcnt, m_field_metadata, m_null_bits)

MTR test rpl_table_map_log_event_overflow covers all affected code paths
with 9 test cases using debug injection.
@LukeYL026 LukeYL026 force-pushed the mdev-39689-table-map-log-event-slave-overflow branch from bdc5681 to 06d0757 Compare June 25, 2026 00:12
@LukeYL026 LukeYL026 changed the base branch from preview-13.1-preview to 10.6 June 25, 2026 00:31
@LukeYL026

Copy link
Copy Markdown
Author

First of all: please rebase on the lowest affected version. This is hardening of the log parser(s), so a bug fix as such. I'd do 10.6, if affected.

Done!

I confirmed by checking code and running added MTR test cases for this fix against 10.6 (oldest LTS), and confirmed that this version is also affected.

I updated the changes to fit version 10.6, and re-ran MTR to confirm that issue has been fixed, and no regressions were introduced.

Also, if this helps with merge process to 13.1, I kept a branch for 13.1 version of this fix in my fork.

@gkodinov gkodinov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Thanks for addressing my remarks!
Please stand by for the final review.

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.

Development

Successfully merging this pull request may close these issues.

5 participants