Skip to content

Commit a0eefdf

Browse files
committed
After-merge fix: Actually apply the changes
The merge a8ed0f7 was accidentally a null-merge. MDEV-18046: Assortment of crashes, assertion failures and ASAN errors in mysql_show_binlog_events
1 parent a8ed0f7 commit a0eefdf

8 files changed

+208
-28
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
RESET MASTER;
2+
call mtr.add_suppression("Error in Log_event::read_log_event*");
3+
call mtr.add_suppression("Replication event checksum verification failed while reading from a log file*");
4+
DROP TABLE /*! IF EXISTS*/ t1;
5+
Warnings:
6+
Note 1051 Unknown table 'test.t1'
7+
CREATE TABLE `t1` (
8+
`col_int` int,
9+
pk integer auto_increment,
10+
`col_char_12_key` char(12),
11+
`col_int_key` int,
12+
`col_char_12` char(12),
13+
primary key (pk),
14+
key (`col_char_12_key` ),
15+
key (`col_int_key` )) ENGINE=InnoDB;
16+
INSERT /*! IGNORE */ INTO t1 VALUES (183173120, NULL, 'abcd', 1, 'efghijk'), (1, NULL, 'lmno', 2, 'p');
17+
ALTER TABLE `t1` ENABLE KEYS;
18+
DROP TABLE t1;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
RESET MASTER;
2+
call mtr.add_suppression("Error in Log_event::read_log_event*");
3+
call mtr.add_suppression("Replication event checksum verification failed while reading from a log file*");
4+
DROP TABLE IF EXISTS t1;
5+
Warnings:
6+
Note 1051 Unknown table 'test.t1'
7+
CREATE TABLE t1 (c1 CHAR(255), c2 CHAR(255), c3 CHAR(255), c4 CHAR(255), c5 CHAR(255));
8+
INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
9+
INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
10+
UPDATE t1 SET c1=repeat('b',255);
11+
INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
12+
DROP TABLE t1;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[enable_checksum]
2+
binlog_checksum=1
3+
4+
[disable_checksum]
5+
binlog_checksum=0
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# ==== Purpose ====
2+
#
3+
# Test verifies that reading binary log by using "SHOW BINLOG EVENTS" command
4+
# from various random positions doesn't lead to crash. It should exit
5+
# gracefully with appropriate error.
6+
#
7+
# ==== References ====
8+
#
9+
# MDEV-18046:Assortment of crashes, assertion failures and ASAN errors in mysql_show_binlog_events
10+
11+
--source include/have_log_bin.inc
12+
--source include/have_innodb.inc
13+
14+
RESET MASTER;
15+
16+
call mtr.add_suppression("Error in Log_event::read_log_event*");
17+
call mtr.add_suppression("Replication event checksum verification failed while reading from a log file*");
18+
19+
DROP TABLE /*! IF EXISTS*/ t1;
20+
CREATE TABLE `t1` (
21+
`col_int` int,
22+
pk integer auto_increment,
23+
`col_char_12_key` char(12),
24+
`col_int_key` int,
25+
`col_char_12` char(12),
26+
primary key (pk),
27+
key (`col_char_12_key` ),
28+
key (`col_int_key` )) ENGINE=InnoDB;
29+
30+
INSERT /*! IGNORE */ INTO t1 VALUES (183173120, NULL, 'abcd', 1, 'efghijk'), (1, NULL, 'lmno', 2, 'p');
31+
32+
--disable_warnings
33+
ALTER TABLE `t1` ENABLE KEYS;
34+
--enable_warnings
35+
36+
--let $max_pos= query_get_value(SHOW MASTER STATUS,Position,1)
37+
--let $pos= 1
38+
while ($pos <= $max_pos)
39+
{
40+
--disable_query_log
41+
--disable_result_log
42+
--error 0,1220
43+
eval SHOW BINLOG EVENTS FROM $pos;
44+
--inc $pos
45+
--enable_result_log
46+
--enable_query_log
47+
}
48+
DROP TABLE t1;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[enable_checksum]
2+
binlog_checksum=1
3+
4+
[disable_checksum]
5+
binlog_checksum=0
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# ==== Purpose ====
2+
#
3+
# Test verifies that reading binary log by using "SHOW BINLOG EVENTS" command
4+
# from various random positions doesn't lead to crash. It should exit
5+
# gracefully with appropriate error.
6+
#
7+
# ==== References ====
8+
#
9+
# MDEV-18046:Assortment of crashes, assertion failures and ASAN errors in mysql_show_binlog_events
10+
11+
--source include/have_log_bin.inc
12+
--source include/have_innodb.inc
13+
14+
RESET MASTER;
15+
call mtr.add_suppression("Error in Log_event::read_log_event*");
16+
call mtr.add_suppression("Replication event checksum verification failed while reading from a log file*");
17+
18+
DROP TABLE IF EXISTS t1;
19+
CREATE TABLE t1 (c1 CHAR(255), c2 CHAR(255), c3 CHAR(255), c4 CHAR(255), c5 CHAR(255));
20+
INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
21+
INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
22+
UPDATE t1 SET c1=repeat('b',255);
23+
INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
24+
--let $max_pos= query_get_value(SHOW MASTER STATUS,Position,1)
25+
--let $pos= 1
26+
while ($pos <= $max_pos)
27+
{
28+
--disable_query_log
29+
--disable_result_log
30+
--error 0,1220
31+
eval SHOW BINLOG EVENTS FROM $pos LIMIT 100;
32+
--inc $pos
33+
--enable_result_log
34+
--enable_query_log
35+
}
36+
37+
DROP TABLE t1;

sql/log_event.cc

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -569,9 +569,14 @@ row_log_event_uncompress(const Format_description_log_event *description_event,
569569

570570
uint32 binlog_get_uncompress_len(const char *buf)
571571
{
572-
DBUG_ASSERT((buf[0] & 0xe0) == 0x80);
573-
uint32 lenlen = buf[0] & 0x07;
574572
uint32 len = 0;
573+
uint32 lenlen = 0;
574+
575+
if ((buf == NULL) || ((buf[0] & 0xe0) != 0x80))
576+
return len;
577+
578+
lenlen = buf[0] & 0x07;
579+
575580
switch(lenlen)
576581
{
577582
case 1:
@@ -1385,6 +1390,17 @@ code_name(int code)
13851390
}
13861391
#endif
13871392

1393+
#define VALIDATE_BYTES_READ(CUR_POS, START, EVENT_LEN) \
1394+
do { \
1395+
uchar *cur_pos= (uchar *)CUR_POS; \
1396+
uchar *start= (uchar *)START; \
1397+
uint len= EVENT_LEN; \
1398+
uint bytes_read= (uint)(cur_pos - start); \
1399+
DBUG_PRINT("info", ("Bytes read: %u event_len:%u.\n",\
1400+
bytes_read, len)); \
1401+
if (bytes_read >= len) \
1402+
DBUG_VOID_RETURN; \
1403+
} while (0)
13881404

13891405
/**
13901406
Macro to check that there is enough space to read from memory.
@@ -1396,7 +1412,6 @@ code_name(int code)
13961412
#define CHECK_SPACE(PTR,END,CNT) \
13971413
do { \
13981414
DBUG_PRINT("info", ("Read %s", code_name(pos[-1]))); \
1399-
DBUG_ASSERT((PTR) + (CNT) <= (END)); \
14001415
if ((PTR) + (CNT) > (END)) { \
14011416
DBUG_PRINT("info", ("query= 0")); \
14021417
query= 0; \
@@ -1731,7 +1746,9 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
17311746

17321747
uint32 max_length= uint32(event_len - ((const char*)(end + db_len + 1) -
17331748
(buf - common_header_len)));
1734-
if (q_len != max_length)
1749+
if (q_len != max_length ||
1750+
(event_len < uint((const char*)(end + db_len + 1) -
1751+
(buf - common_header_len))))
17351752
{
17361753
q_len= 0;
17371754
query= NULL;
@@ -2429,6 +2446,8 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
24292446
{
24302447
DBUG_ENTER("Load_log_event::copy_log_event");
24312448
uint data_len;
2449+
if ((int) event_len < body_offset)
2450+
DBUG_RETURN(1);
24322451
char* buf_end = (char*)buf + event_len;
24332452
/* this is the beginning of the post-header */
24342453
const char* data_head = buf + description_event->common_header_len;
@@ -2438,9 +2457,7 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
24382457
table_name_len = (uint)data_head[L_TBL_LEN_OFFSET];
24392458
db_len = (uint)data_head[L_DB_LEN_OFFSET];
24402459
num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET);
2441-
2442-
if ((int) event_len < body_offset)
2443-
DBUG_RETURN(1);
2460+
24442461
/*
24452462
Sql_ex.init() on success returns the pointer to the first byte after
24462463
the sql_ex structure, which is the start of field lengths array.
@@ -2449,7 +2466,7 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
24492466
buf_end,
24502467
(uchar)buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
24512468
DBUG_RETURN(1);
2452-
2469+
24532470
data_len = event_len - body_offset;
24542471
if (num_fields > data_len) // simple sanity check against corruption
24552472
DBUG_RETURN(1);
@@ -2493,7 +2510,7 @@ Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
24932510
// The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
24942511
uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
24952512
uint ident_offset;
2496-
if (event_len < LOG_EVENT_MINIMAL_HEADER_LEN)
2513+
if (event_len < (uint)(LOG_EVENT_MINIMAL_HEADER_LEN + post_header_len))
24972514
DBUG_VOID_RETURN;
24982515
buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
24992516
pos= post_header_len ? uint8korr(buf + R_POS_OFFSET) : 4;
@@ -2833,6 +2850,11 @@ User_var_log_event(const char* buf, uint event_len,
28332850
we keep the flags set to UNDEF_F.
28342851
*/
28352852
size_t bytes_read= (val + val_len) - buf_start;
2853+
if (bytes_read > event_len)
2854+
{
2855+
error= true;
2856+
goto err;
2857+
}
28362858
if ((data_written - bytes_read) > 0)
28372859
{
28382860
flags= (uint) *(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
@@ -3099,7 +3121,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
30993121
uint8 const common_header_len= description_event->common_header_len;
31003122
Log_event_type event_type= (Log_event_type)(uchar)buf[EVENT_TYPE_OFFSET];
31013123
m_type= event_type;
3102-
3124+
m_cols_ai.bitmap= 0;
3125+
31033126
uint8 const post_header_len= description_event->post_header_len[event_type-1];
31043127

31053128
DBUG_PRINT("enter",("event_len: %u common_header_len: %d "
@@ -3133,7 +3156,14 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
31333156
which includes length bytes
31343157
*/
31353158
var_header_len= uint2korr(post_start);
3136-
assert(var_header_len >= 2);
3159+
/* Check length and also avoid out of buffer read */
3160+
if (var_header_len < 2 ||
3161+
event_len < static_cast<unsigned int>(var_header_len +
3162+
(post_start - buf)))
3163+
{
3164+
m_cols.bitmap= 0;
3165+
DBUG_VOID_RETURN;
3166+
}
31373167
var_header_len-= 2;
31383168

31393169
/* Iterate over var-len header, extracting 'chunks' */
@@ -3449,6 +3479,9 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
34493479
DBUG_DUMP("event buffer", (uchar*) buf, event_len);
34503480
#endif
34513481

3482+
if (event_len < (uint)(common_header_len + post_header_len))
3483+
DBUG_VOID_RETURN;
3484+
34523485
/* Read the post-header */
34533486
const char *post_start= buf + common_header_len;
34543487

@@ -3475,15 +3508,18 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
34753508

34763509
/* Extract the length of the various parts from the buffer */
34773510
uchar const *const ptr_dblen= (uchar const*)vpart + 0;
3511+
VALIDATE_BYTES_READ(ptr_dblen, buf, event_len);
34783512
m_dblen= *(uchar*) ptr_dblen;
34793513

34803514
/* Length of database name + counter + terminating null */
34813515
uchar const *const ptr_tbllen= ptr_dblen + m_dblen + 2;
3516+
VALIDATE_BYTES_READ(ptr_tbllen, buf, event_len);
34823517
m_tbllen= *(uchar*) ptr_tbllen;
34833518

34843519
/* Length of table name + counter + terminating null */
34853520
uchar const *const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
34863521
uchar *ptr_after_colcnt= (uchar*) ptr_colcnt;
3522+
VALIDATE_BYTES_READ(ptr_after_colcnt, buf, event_len);
34873523
m_colcnt= net_field_length(&ptr_after_colcnt);
34883524

34893525
DBUG_PRINT("info",("m_dblen: %lu off: %ld m_tbllen: %lu off: %ld m_colcnt: %lu off: %ld",
@@ -3506,22 +3542,27 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
35063542
memcpy(m_coltype, ptr_after_colcnt, m_colcnt);
35073543

35083544
ptr_after_colcnt= ptr_after_colcnt + m_colcnt;
3509-
bytes_read= (uint) (ptr_after_colcnt - (uchar *)buf);
3510-
DBUG_PRINT("info", ("Bytes read: %d", bytes_read));
3511-
if (bytes_read < event_len)
3545+
VALIDATE_BYTES_READ(ptr_after_colcnt, buf, event_len);
3546+
m_field_metadata_size= net_field_length(&ptr_after_colcnt);
3547+
if (m_field_metadata_size <= (m_colcnt * 2))
35123548
{
3513-
m_field_metadata_size= net_field_length(&ptr_after_colcnt);
3514-
DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));
35153549
uint num_null_bytes= (m_colcnt + 7) / 8;
35163550
m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
3517-
&m_null_bits, num_null_bytes,
3518-
&m_field_metadata, m_field_metadata_size,
3519-
NULL);
3551+
&m_null_bits, num_null_bytes,
3552+
&m_field_metadata, m_field_metadata_size,
3553+
NULL);
35203554
memcpy(m_field_metadata, ptr_after_colcnt, m_field_metadata_size);
35213555
ptr_after_colcnt= (uchar*)ptr_after_colcnt + m_field_metadata_size;
35223556
memcpy(m_null_bits, ptr_after_colcnt, num_null_bytes);
35233557
ptr_after_colcnt= (unsigned char*)ptr_after_colcnt + num_null_bytes;
35243558
}
3559+
else
3560+
{
3561+
m_coltype= NULL;
3562+
my_free(m_memory);
3563+
m_memory= NULL;
3564+
DBUG_VOID_RETURN;
3565+
}
35253566

35263567
bytes_read= (uint) (ptr_after_colcnt - (uchar *)buf);
35273568

@@ -3843,9 +3884,12 @@ Delete_rows_compressed_log_event::Delete_rows_compressed_log_event(
38433884

38443885
Update_rows_log_event::~Update_rows_log_event()
38453886
{
3846-
if (m_cols_ai.bitmap == m_bitbuf_ai) // no my_malloc happened
3847-
m_cols_ai.bitmap= 0; // so no my_free in my_bitmap_free
3848-
my_bitmap_free(&m_cols_ai); // To pair with my_bitmap_init().
3887+
if (m_cols_ai.bitmap)
3888+
{
3889+
if (m_cols_ai.bitmap == m_bitbuf_ai) // no my_malloc happened
3890+
m_cols_ai.bitmap= 0; // so no my_free in my_bitmap_free
3891+
my_bitmap_free(&m_cols_ai); // To pair with my_bitmap_init().
3892+
}
38493893
}
38503894

38513895

sql/sql_repl.cc

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3975,6 +3975,11 @@ bool mysql_show_binlog_events(THD* thd)
39753975
List<Item> field_list;
39763976
const char *errmsg = 0;
39773977
bool ret = TRUE;
3978+
/*
3979+
Using checksum validate the correctness of event pos specified in show
3980+
binlog events command.
3981+
*/
3982+
bool verify_checksum_once= false;
39783983
IO_CACHE log;
39793984
File file = -1;
39803985
MYSQL_BIN_LOG *binary_log= NULL;
@@ -4030,6 +4035,10 @@ bool mysql_show_binlog_events(THD* thd)
40304035
mi= 0;
40314036
}
40324037

4038+
/* Validate user given position using checksum */
4039+
if (lex_mi->pos == pos && !opt_master_verify_checksum)
4040+
verify_checksum_once= true;
4041+
40334042
unit->set_limit(thd->lex->current_select);
40344043

40354044
name= search_file_name;
@@ -4107,15 +4116,16 @@ bool mysql_show_binlog_events(THD* thd)
41074116
for (event_count = 0;
41084117
(ev = Log_event::read_log_event(&log,
41094118
description_event,
4110-
opt_master_verify_checksum)); )
4119+
(opt_master_verify_checksum ||
4120+
verify_checksum_once))); )
41114121
{
41124122
if (!unit->lim.check_offset(event_count) &&
4113-
ev->net_send(protocol, linfo.log_file_name, pos))
4123+
ev->net_send(protocol, linfo.log_file_name, pos))
41144124
{
4115-
errmsg = "Net error";
4116-
delete ev;
4125+
errmsg = "Net error";
4126+
delete ev;
41174127
mysql_mutex_unlock(log_lock);
4118-
goto err;
4128+
goto err;
41194129
}
41204130

41214131
if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
@@ -4141,10 +4151,11 @@ bool mysql_show_binlog_events(THD* thd)
41414151
delete ev;
41424152
}
41434153

4154+
verify_checksum_once= false;
41444155
pos = my_b_tell(&log);
41454156

41464157
if (++event_count >= unit->lim.get_select_limit())
4147-
break;
4158+
break;
41484159
}
41494160

41504161
if (unlikely(event_count < unit->lim.get_select_limit() && log.error))

0 commit comments

Comments
 (0)