Skip to content

Commit 3fb2f8d

Browse files
author
Joao Gramacho
committed
BUG#24365972 BINLOG DECODING ISN'T RESILIENT TO CORRUPT BINLOG FILES
Problem ======= When facing decoding of corrupt binary log files, server may misbehave without detecting the events corruption. This patch makes MySQL server more resilient to binary log decoding. Fixes for events de-serialization and apply =========================================== @sql/log_event.cc Query_log_event::Query_log_event: added a check to ensure query length is respecting event buffer limits. Query_log_event::do_apply_event: extended a debug print, added a check to character set to determine if it is "parseable" or not, verified if database name is valid for system collation. Start_log_event_v3::do_apply_event: report an error on applying a non-supported binary log version. Load_log_event::copy_log_event: added a check to table_name length. User_var_log_event::User_var_log_event: added checks to avoid reading out of buffer limits. User_var_log_event::do_apply_event: reported an sanity check error properly and added individual sanity checks for variable types that expect fixed (or minimum) amount of bytes to be read. Rows_log_event::Rows_log_event: added checks to avoid reading out of buffer limits. @sql/log_event_old.cc Old_rows_log_event::Old_rows_log_event: added a sanity check to avoid reading out of buffer limits. @sql/sql_priv.h Added a sanity check to available_buffer() function.
1 parent 2af9e8a commit 3fb2f8d

File tree

3 files changed

+136
-8
lines changed

3 files changed

+136
-8
lines changed

sql/log_event.cc

Lines changed: 120 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -3024,6 +3024,25 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
30243024
db= (char *)start;
30253025
query= (char *)(start + db_len + 1);
30263026
q_len= data_len - db_len -1;
3027+
3028+
if (data_len && (data_len < db_len ||
3029+
data_len < q_len ||
3030+
data_len != (db_len + q_len + 1)))
3031+
{
3032+
q_len= 0;
3033+
query= NULL;
3034+
DBUG_VOID_RETURN;
3035+
}
3036+
3037+
unsigned int max_length;
3038+
max_length= (event_len - ((const char*)(end + db_len + 1) -
3039+
(buf - common_header_len)));
3040+
if (q_len != max_length)
3041+
{
3042+
q_len= 0;
3043+
query= NULL;
3044+
DBUG_VOID_RETURN;
3045+
}
30273046
/**
30283047
Append the db length at the end of the buffer. This will be used by
30293048
Query_cache::send_result_to_client() in case the query cache is On.
@@ -3278,6 +3297,26 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
32783297
you.
32793298
*/
32803299
thd->catalog= catalog_len ? (char *) catalog : (char *)"";
3300+
3301+
size_t valid_len;
3302+
bool len_error;
3303+
bool is_invalid_db_name= validate_string(system_charset_info, db, db_len,
3304+
&valid_len, &len_error);
3305+
3306+
DBUG_PRINT("debug",("is_invalid_db_name= %s, valid_len=%zu, len_error=%s",
3307+
is_invalid_db_name ? "true" : "false",
3308+
valid_len,
3309+
len_error ? "true" : "false"));
3310+
3311+
if (is_invalid_db_name || len_error)
3312+
{
3313+
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
3314+
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
3315+
"Invalid database name in Query event.");
3316+
thd->is_slave_error= true;
3317+
goto end;
3318+
}
3319+
32813320
new_db.length= db_len;
32823321
new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
32833322
thd->set_db(new_db.str, new_db.length); /* allocates a copy of 'db' */
@@ -3454,7 +3493,23 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
34543493
}
34553494
else
34563495
thd->variables.collation_database= thd->db_charset;
3457-
3496+
3497+
{
3498+
const CHARSET_INFO *cs= thd->charset();
3499+
/*
3500+
We cannot ask for parsing a statement using a character set
3501+
without state_maps (parser internal data).
3502+
*/
3503+
if (!cs->state_map)
3504+
{
3505+
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
3506+
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
3507+
"character_set cannot be parsed");
3508+
thd->is_slave_error= true;
3509+
goto end;
3510+
}
3511+
}
3512+
34583513
thd->table_map_for_update= (table_map)table_map_for_update;
34593514
thd->set_invoker(&user, &host);
34603515
/*
@@ -3898,7 +3953,13 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
38983953
*/
38993954
break;
39003955
default:
3901-
/* this case is impossible */
3956+
/*
3957+
This case is not expected. It can be either an event corruption or an
3958+
unsupported binary log version.
3959+
*/
3960+
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
3961+
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
3962+
"Binlog version not supported");
39023963
DBUG_RETURN(1);
39033964
}
39043965
DBUG_RETURN(error);
@@ -4724,6 +4785,9 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
47244785

47254786
fields = (char*)field_lens + num_fields;
47264787
table_name = fields + field_block_len;
4788+
if (strlen(table_name) > NAME_LEN)
4789+
goto err;
4790+
47274791
db = table_name + table_name_len + 1;
47284792
DBUG_EXECUTE_IF ("simulate_invalid_address",
47294793
db_len = data_len;);
@@ -5889,6 +5953,13 @@ User_var_log_event(const char* buf, uint event_len,
58895953
buf+= description_event->common_header_len +
58905954
description_event->post_header_len[USER_VAR_EVENT-1];
58915955
name_len= uint4korr(buf);
5956+
/* Avoid reading out of buffer */
5957+
if ((buf - buf_start) + UV_NAME_LEN_SIZE + name_len > event_len)
5958+
{
5959+
error= true;
5960+
goto err;
5961+
}
5962+
58925963
name= (char *) buf + UV_NAME_LEN_SIZE;
58935964

58945965
/*
@@ -5948,8 +6019,11 @@ User_var_log_event(const char* buf, uint event_len,
59486019
we keep the flags set to UNDEF_F.
59496020
*/
59506021
uint bytes_read= ((val + val_len) - start);
5951-
DBUG_ASSERT(bytes_read==data_written ||
5952-
bytes_read==(data_written-1));
6022+
if (bytes_read > event_len)
6023+
{
6024+
error= true;
6025+
goto err;
6026+
}
59536027
if ((data_written - bytes_read) > 0)
59546028
{
59556029
flags= (uint) *(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
@@ -6165,7 +6239,12 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
61656239
}
61666240

61676241
if (!(charset= get_charset(charset_number, MYF(MY_WME))))
6242+
{
6243+
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
6244+
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
6245+
"Invalid character set for User var event");
61686246
return 1;
6247+
}
61696248
LEX_STRING user_var_name;
61706249
user_var_name.str= name;
61716250
user_var_name.length= name_len;
@@ -6186,19 +6265,40 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
61866265
{
61876266
switch (type) {
61886267
case REAL_RESULT:
6268+
if (val_len != 8)
6269+
{
6270+
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
6271+
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
6272+
"Invalid variable length at User var event");
6273+
return 1;
6274+
}
61896275
float8get(real_val, val);
61906276
it= new Item_float(real_val, 0);
61916277
val= (char*) &real_val; // Pointer to value in native format
61926278
val_len= 8;
61936279
break;
61946280
case INT_RESULT:
6281+
if (val_len != 8)
6282+
{
6283+
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
6284+
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
6285+
"Invalid variable length at User var event");
6286+
return 1;
6287+
}
61956288
int_val= (longlong) uint8korr(val);
61966289
it= new Item_int(int_val);
61976290
val= (char*) &int_val; // Pointer to value in native format
61986291
val_len= 8;
61996292
break;
62006293
case DECIMAL_RESULT:
62016294
{
6295+
if (val_len < 3)
6296+
{
6297+
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
6298+
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
6299+
"Invalid variable length at User var event");
6300+
return 1;
6301+
}
62026302
Item_decimal *dec= new Item_decimal((uchar*) val+2, val[0], val[1]);
62036303
it= dec;
62046304
val= (char *)dec->val_decimal(NULL);
@@ -7646,6 +7746,15 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
76467746
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
76477747
m_width = net_field_length(&ptr_after_width);
76487748
DBUG_PRINT("debug", ("m_width=%lu", m_width));
7749+
/* Avoid reading out of buffer */
7750+
if (static_cast<unsigned int>((ptr_after_width +
7751+
(m_width + 7) / 8) -
7752+
(uchar*)buf) > event_len)
7753+
{
7754+
m_cols.bitmap= NULL;
7755+
DBUG_VOID_RETURN;
7756+
}
7757+
76497758
/* if bitmap_init fails, catched in is_valid() */
76507759
if (likely(!bitmap_init(&m_cols,
76517760
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
@@ -7694,7 +7803,12 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
76947803

76957804
const uchar* const ptr_rows_data= (const uchar*) ptr_after_width;
76967805

7697-
size_t const data_size= event_len - (ptr_rows_data - (const uchar *) buf);
7806+
size_t const read_size= ptr_rows_data - (const unsigned char *) buf;
7807+
if (read_size > event_len)
7808+
{
7809+
DBUG_VOID_RETURN;
7810+
}
7811+
size_t const data_size= event_len - read_size;
76987812
DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %lu",
76997813
m_table_id, m_flags, m_width, (ulong) data_size));
77007814

sql/log_event_old.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -1357,6 +1357,15 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len,
13571357
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
13581358
m_width = net_field_length(&ptr_after_width);
13591359
DBUG_PRINT("debug", ("m_width=%lu", m_width));
1360+
/* Avoid reading out of buffer */
1361+
if (static_cast<unsigned int>(m_width +
1362+
(ptr_after_width -
1363+
(const uchar *)buf)) > event_len)
1364+
{
1365+
m_cols.bitmap= NULL;
1366+
DBUG_VOID_RETURN;
1367+
}
1368+
13601369
/* if bitmap_init fails, catched in is_valid() */
13611370
if (likely(!bitmap_init(&m_cols,
13621371
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,

sql/sql_priv.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -191,6 +191,11 @@ template <class T> T available_buffer(const char* buf_start,
191191
const char* buf_current,
192192
T buf_len)
193193
{
194+
/* Sanity check */
195+
if (buf_current < buf_start ||
196+
buf_len < static_cast<T>(buf_current - buf_start))
197+
return static_cast<T>(0);
198+
194199
return buf_len - (buf_current - buf_start);
195200
}
196201

0 commit comments

Comments
 (0)