Details
-
Bug
-
Status: Open (View Workflow)
-
Major
-
Resolution: Unresolved
-
None
-
None
-
Can result in hang or crash
Description
A slave can overflow when uncompressing a
Query_compressed_log_event.
The function query_event_uncompress() reads in a 32-bit uint,
converts it to 64-bit, manipulates the value, casts the value back to
32-bits, and then decides memory allocation strategy based on that
value:
uint32 un_len= binlog_get_uncompress_len(tmp);
|
|
|
// bad event |
if (comp_len < 0 || un_len == 0) |
return 1; |
|
|
*newlen= (ulong)(tmp - src) + un_len;
|
if (contain_checksum) |
*newlen+= BINLOG_CHECKSUM_LEN;
|
|
|
uint32 alloc_size= (uint32)ALIGN_SIZE(*newlen);
|
|
if (alloc_size <= buf_size) |
new_dst= buf;
|
else |
{
|
new_dst= (uchar *) my_malloc(PSI_INSTRUMENT_ME, alloc_size, MYF(MY_WME));
|
if (!new_dst) |
return 1; |
*is_malloc= true; |
}
|
If the 32-bit read-in value is close to the 32-bit max, this can cause
the value to overflow and pick the wrong allocation strategy.
In practice, this cannot happen because the max_allowed_packet for a
server is hard-capped at 1GB, so there is not a valid path for a user to
generate a query that overflow.
The follwing MTR test (with .cnf and patch) can be used to show the
issue.
MTR file
--source include/have_debug.inc
|
--source include/have_binlog_format_statement.inc
|
--source include/master-slave.inc
|
|
|
--echo #
|
--echo # Setup: a query large enough that its decompressed body well exceeds
|
--echo # the 4 KB stack buffer in query_event_uncompress.
|
--connection master
|
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 LONGTEXT);
|
|
--echo #
|
--echo # Arm the corruption: the next compressed query event written will
|
--echo # have its un_len header overwritten to 0xFFFFFFFC.
|
SET @saved_dbug= @@SESSION.debug_dbug;
|
SET @@SESSION.debug_dbug= "+d,query_compressed_corrupt_un_len_overflow";
|
|
|
--echo #
|
--echo # An ~8 KB INSERT statement -- compresses to a few hundred bytes on
|
--echo # the wire, but decompresses back to ~8 KB at the slave, which is
|
--echo # what overruns the 4 KB stack buffer.
|
--let $payload= `SELECT REPEAT('A', 8192)`
|
--eval INSERT INTO t1 VALUES (1, '$payload')
|
|
|
SET @@SESSION.debug_dbug= @saved_dbug;
|
--source include/save_master_gtid.inc
|
|
|
--echo #
|
--echo # The slave's IO thread crashes while uncompressing the malformed
|
--echo # QUERY_COMPRESSED_EVENT. Reaching this sync means the bug did not
|
--echo # fire; on an unfixed build the slave aborts here.
|
--connection slave
|
--source include/sync_with_master_gtid.inc
|
|
|
--echo #
|
--echo # Cleanup
|
--connection master
|
DROP TABLE t1;
|
--source include/rpl_end.inc
|
MTR .cnf
!include ../my.cnf
|
|
|
[mysqld]
|
log_bin_compress=ON
|
log_bin_compress_min_len=10
|
binlog_checksum=NONE
|
Patch file
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
|
index 8652888d744..f96e7f9dc18 100644
|
--- a/sql/log_event_server.cc
|
+++ b/sql/log_event_server.cc
|
@@ -1298,6 +1298,24 @@ bool Query_compressed_log_event::write(Log_event_writer *writer)
|
if (buffer &&
|
!binlog_buf_compress((uchar*) query, buffer, q_len, &compressed_size))
|
{
|
+ DBUG_EXECUTE_IF("query_compressed_corrupt_un_len_overflow",
|
+ {
|
+ /*
|
+ Rewrite the un_len header so it claims a near-UINT32_MAX
|
+ uncompressed length (0xFFFFFFFC), forcing a 4-byte length
|
+ prefix. binlog_get_compress_len reserves room for a 4-byte
|
+ prefix, so we can safely shift the zlib payload right.
|
+ */
|
+ uchar old_lenlen= buffer[0] & 0x07;
|
+ uint32 zlib_data_len= compressed_size - 1 - old_lenlen;
|
+ memmove(buffer + 1 + 4, buffer + 1 + old_lenlen, zlib_data_len);
|
+ buffer[0]= 0x84;
|
+ buffer[1]= 0xFF;
|
+ buffer[2]= 0xFF;
|
+ buffer[3]= 0xFF;
|
+ buffer[4]= 0xFC;
|
+ compressed_size= zlib_data_len + 5;
|
+ });
|
/*
|
Write the compressed event. We have to temporarily store the event
|
in query and q_len as Query_log_event::write() uses these.
|
Note that compressed row events and regular Query events have already
fixed this problem and are not vulnerable. Their pattern should be used
here to fix this.
Reported by Winston Crooker
Attachments
Issue Links
- relates to
-
MDEV-39672 Mariadb-binlog Overflow on Malformed Table_map_log_event
-
- Open
-
-
MDEV-39674 Slave Shows Invalid Data on Malformed Table_map_log_event
-
- Open
-
-
MDEV-39689 Slave Overflow on Malformed Table_map_log_event
-
- Open
-