Skip to content

Commit be132be

Browse files
author
Neha Kumari
committed
Bug#29417234:CONTRIBUTION BY FACEBOOK: FIX WRITING FORMAT DESCRIPTION EVENT
Problem: In the function: Format_description_log_event::write(..) memcpy((char *)buff + ST_COMMON_HEADER_LEN_OFFSET + 1, &post_header_len[0], Binary_log_event::LOG_EVENT_TYPES); This works fine, if the server generating the FDE is the same as the server writing it out (hence post_header_len.size == Binary_log_event::LOG_EVENT_TYPES). However, when we rotate relay logs, we use this function to write out the FDE by using the event obtained from the master directly. If the master is on an older version of MySQL with not as many event types, then this memcpy will read beyond its buffer causing an ASAN failure. Analysis: Different versions of mysqld have different number of binlog event types. The Format_description_event log has an post_header_len whose size is based on the number of events supported by the mysqld version. So a 5.6 mysqld generates a FDE with 35 types while 5.7 mysqld generates a FDE with 38 types. When 5.7 mysqld attempts to read a 5.6 generated FDE and then rewrite out the 5.6 FDE, it uses its own value of LOG_EVENT_TYPES (38) to determine the length of the array, which is actually only 35 types. The copy accesses undefined memory to generate the FDE. Fix: if the post_header_len is greater than Binary_log_event::LOG_EVENT_TYPES, use the sizeof post_header_len vector while writing post_header_len vector, otherwise use Binary_log_event::LOG_EVENT_TYPES. P.S: This patch also corrects another issue which happens while decoding the post_header_len array in FDE. While decoding post_header_len we use the variable number_of_event_types to determine how many bytes to read from the buffer, now at this point the number_of_event_types holds the value which is one more than the actual value. This problem happens because we don't consider the "BINLOG_CHECKSUM_ALG_DESC_LEN" before decoding post_header_len. In 8.0 this issue has already been corrected(WL#11567) so this part of change is just for 5.7. RB: 21849
1 parent ec39bef commit be132be

File tree

7 files changed

+113
-11
lines changed

7 files changed

+113
-11
lines changed

libbinlogevents/src/control_events.cpp

+12-9
Original file line numberDiff line numberDiff line change
@@ -335,26 +335,29 @@ Format_description_event(const char* buf, unsigned int event_len,
335335
number_of_event_types=
336336
event_len - (LOG_EVENT_MINIMAL_HEADER_LEN + ST_COMMON_HEADER_LEN_OFFSET + 1);
337337

338-
const uint8_t *ubuf = reinterpret_cast<const uint8_t*>(buf);
338+
ver_calc = get_product_version();
339+
if (ver_calc >= checksum_version_product) {
340+
/* the last bytes are the checksum alg desc and value (or value's room) */
341+
number_of_event_types -= BINLOG_CHECKSUM_ALG_DESC_LEN;
342+
}
343+
344+
const uint8_t *ubuf = reinterpret_cast<const uint8_t*>(buf) +
345+
ST_COMMON_HEADER_LEN_OFFSET + 1;
339346
post_header_len.insert(post_header_len.begin(),
340-
ubuf + ST_COMMON_HEADER_LEN_OFFSET + 1,
341-
(ubuf + ST_COMMON_HEADER_LEN_OFFSET + 1 +
342-
number_of_event_types));
347+
ubuf, (ubuf + number_of_event_types));
343348

349+
ubuf += number_of_event_types;
344350
calc_server_version_split();
345-
if ((ver_calc= get_product_version()) >= checksum_version_product)
351+
if (ver_calc >= checksum_version_product)
346352
{
347-
/* the last bytes are the checksum alg desc and value (or value's room) */
348-
number_of_event_types -= BINLOG_CHECKSUM_ALG_DESC_LEN;
349353
/*
350354
FD from the checksum-home version server (ver_calc ==
351355
checksum_version_product) must have
352356
number_of_event_types == LOG_EVENT_TYPES.
353357
*/
354358
BAPI_ASSERT(ver_calc != checksum_version_product ||
355359
number_of_event_types == LOG_EVENT_TYPES);
356-
footer()->checksum_alg= (enum_binlog_checksum_alg)
357-
post_header_len[number_of_event_types];
360+
footer()->checksum_alg= static_cast<enum_binlog_checksum_alg> (*ubuf);
358361
}
359362
else
360363
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.\master-bin.000001
545 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./master-bin.000001
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
include/master-slave.inc
2+
Warnings:
3+
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
4+
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
5+
[connection master]
6+
RESET MASTER;
7+
[connection slave]
8+
CALL mtr.add_suppression("The slave coordinator and worker threads are stopped, possibly leaving data in inconsistent state");
9+
CALL mtr.add_suppression("Got fatal error 1236 from master when reading data from binary log:");
10+
START SLAVE;
11+
include/wait_for_slave_param.inc [Slave_IO_State]
12+
FLUSH LOGS;
13+
STOP SLAVE;
14+
RESET SLAVE;
15+
include/rpl_end.inc
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# ==== Purpose ====
2+
#
3+
# Tests the statement based replication of tables in a cross version setup
4+
# where the master is a 8.0 server and slave is a 5.7 server, and verify
5+
# that there is no crash at the time of FLUSH LOGS on slave side.
6+
#
7+
# ==== Implementation ====
8+
#
9+
# Start a master server on 5.7
10+
# Copy the binary log file and index of a 8.0 master server to the datadir of 5.7 master server
11+
# Start the slave on a 5.7 server
12+
# Execute Flush logs command on the slave
13+
# Without the fix the Flush Logs command will trigger an ASAN failure
14+
#
15+
# ==== References ====
16+
#
17+
# Bug#29417234:CONTRIBUTION BY FACEBOOK: FIX WRITING FORMAT DESCRIPTION EVENT
18+
#
19+
20+
--source include/have_binlog_format_statement.inc
21+
--source include/not_gtid_enabled.inc
22+
--let $rpl_skip_start_slave= 1
23+
--source include/master-slave.inc
24+
25+
--let $MYSQLD_MASTER_DATADIR= `select @@datadir`
26+
27+
# clear master datadir
28+
RESET MASTER;
29+
--remove_file $MYSQLD_MASTER_DATADIR/master-bin.000001
30+
--remove_file $MYSQLD_MASTER_DATADIR/master-bin.index
31+
32+
# on Win* platforms path separator is backslash
33+
if (`SELECT CONVERT(@@VERSION_COMPILE_OS USING latin1) IN ('Win32', 'Win64', 'Windows')`)
34+
{
35+
--copy_file std_data/slave-relay-bin-win.index $MYSQLD_MASTER_DATADIR/master-bin.index
36+
}
37+
if (`SELECT CONVERT(@@VERSION_COMPILE_OS USING latin1) NOT IN ('Win32', 'Win64', 'Windows')`)
38+
{
39+
--copy_file std_data/slave-relay-bin.index $MYSQLD_MASTER_DATADIR/master-bin.index
40+
}
41+
42+
--copy_file std_data/slave-relay-bin.000001 $MYSQLD_MASTER_DATADIR/master-bin.000001
43+
44+
--source include/rpl_connection_slave.inc
45+
CALL mtr.add_suppression("The slave coordinator and worker threads are stopped, possibly leaving data in inconsistent state");
46+
CALL mtr.add_suppression("Got fatal error 1236 from master when reading data from binary log:");
47+
START SLAVE;
48+
--let $slave_param= Slave_IO_State
49+
--let $slave_param_value = Waiting for master to send event
50+
--source include/wait_for_slave_param.inc
51+
52+
FLUSH LOGS;
53+
STOP SLAVE;
54+
RESET SLAVE;
55+
56+
--let $rpl_only_running_threads= 1
57+
--source include/rpl_end.inc

sql/log_event.cc

+27-2
Original file line numberDiff line numberDiff line change
@@ -5522,8 +5522,33 @@ bool Format_description_log_event::write(IO_CACHE* file)
55225522
created= get_time();
55235523
int4store(buff + ST_CREATED_OFFSET, static_cast<uint32>(created));
55245524
buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
5525-
memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET + 1, &post_header_len.front(),
5526-
Binary_log_event::LOG_EVENT_TYPES);
5525+
5526+
size_t number_of_events;
5527+
if (post_header_len.size() == Binary_log_event::LOG_EVENT_TYPES)
5528+
// Replicating between master and slave with same version.
5529+
// number_of_events will be same as Binary_log_event::LOG_EVENT_TYPES
5530+
number_of_events = Binary_log_event::LOG_EVENT_TYPES;
5531+
else if (post_header_len.size() > Binary_log_event::LOG_EVENT_TYPES)
5532+
/*
5533+
Replicating between new master and old slave.
5534+
In that case there won't be any memory issues, as there won't be
5535+
any out of memory read.
5536+
*/
5537+
number_of_events = Binary_log_event::LOG_EVENT_TYPES;
5538+
else
5539+
/*
5540+
Replicating between old master and new slave.
5541+
In that case it might lead to different number_of_events on master and
5542+
slave. When the relay log is rotated, the FDE from master is used to
5543+
create the FDE event on slave, which is being written here. In that case
5544+
we might end up reading more bytes as
5545+
post_header_len.size() < Binary_log_event::LOG_EVENT_TYPES;
5546+
casuing memory issues.
5547+
*/
5548+
number_of_events = post_header_len.size();
5549+
5550+
memcpy((char*) buff + ST_COMMON_HEADER_LEN_OFFSET + 1, &post_header_len.front(),
5551+
number_of_events);
55275552
/*
55285553
if checksum is requested
55295554
record the checksum-algorithm descriptor next to

0 commit comments

Comments
 (0)