Details
-
Bug
-
Status: In Progress (View Workflow)
-
Blocker
-
Resolution: Unresolved
-
None
-
Can result in hang or crash
-
Q3/2026 Replic. Development
Description
A replication source can overflow a heap buffer on its replica by sending a
Table_map event that declares an over-long database or table name.
The table name and database name are allocated length NAME_LEN+1
if (!(memory= my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME), |
&table_list, (uint) sizeof(RPL_TABLE_LIST), |
&db_mem, (uint) NAME_LEN + 1,
|
&tname_mem, (uint) NAME_LEN + 1,
|
NullS)))
|
Table_map_log_event::do_apply_event() copies the names of the table
and database directly into this memory without bounds checking:
db_mem_length= strmov(db_mem, m_dbnam) - db_mem;
|
tname_mem_length= strmov(tname_mem, m_tblnam) - tname_mem;
|
NAME_LEN is 192, so each block is requested at 193 bytes and rounded to
200 by my_multi_malloc. A declared length of 255 therefore writes 256
bytes into 200. db_mem is the middle block, so overflowing it corrupts
tname_mem inside the same allocation; tname_mem is the last block, so
overflowing it writes 56 bytes past the end of the region.
A second defect makes the overflow unbounded rather than 56 bytes.
strncpy(m_dbnam, ptr_dblen + 1, m_dblen + 1)
(sql/log_event.cc:3754) does not terminate when none of the source bytes
is zero. With both lengths at 255 the ALIGN_SIZE rounding leaves
m_dbnam, m_tblnam and m_coltype contiguous with no intervening
zero byte, so strmov runs through all three and past the end of
m_memory until it meets a zero somewhere in the heap.
The same missing terminator lets Table_map_log_event::pack_info()
(sql/log_event_server.cc:6161) read past m_memory through a %s,
disclosing heap bytes through SHOW BINLOG EVENTS and SHOW RELAYLOG EVENTS.
To reproduce:
Patch file:
--- a/sql/log_event_server.cc
|
+++ b/sql/log_event_server.cc
|
@@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ...)
|
DBUG_ASSERT(tbl->s->table_name.str[tbl->s->table_name.length] == 0);
|
|
|
+ DBUG_EXECUTE_IF("corrupt_table_map_tbllen", |
+ {
|
+ char *nam= (char*) thd->alloc(256); |
+ memset(nam, 'a', 255); |
+ nam[255]= 0;
|
+ m_tblnam= nam;
|
+ m_tbllen= 255;
|
+ });
|
+
|
binlog_type_info_array= ...
|
|
|
@@ Table_map_log_event::write_data_body()
|
- DBUG_ASSERT(m_tbllen <= MY_MIN(NAME_LEN, 255));
|
+ DBUG_ASSERT(m_tbllen <= MY_MIN(NAME_LEN, 255) ||
|
+ DBUG_EVALUATE_IF("corrupt_table_map_tbllen", 1, 0)); |
Test file:
--source include/have_debug.inc
|
--source include/have_binlog_format_row.inc
|
--source include/master-slave.inc
|
|
|
--connection master
|
create table t1 (a int);
|
set @@session.debug_dbug= "+d,corrupt_table_map_tbllen";
|
insert into t1 values (1);
|
set @@session.debug_dbug= "";
|
--source include/save_master_gtid.inc
|
|
|
# do_apply_event() copies the 256-byte name into a 193-byte block. The sync
|
# below only holds the test open; the replica dies in the strmov.
|
--connection slave
|
--source include/sync_with_master_gtid.inc
|
Attachments
Issue Links
- relates to
-
MDEV-40646 Slave Crashes in table_def::table_def() on Malformed Table_map
-
- In Progress
-
-
MDEV-39689 Slave Overflow on Malformed Table_map_log_event
-
- Closed
-