Commit 3fb2f8db authored by Joao Gramacho's avatar Joao Gramacho

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.
parent 2af9e8af
/* /*
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by 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, ...@@ -3024,6 +3024,25 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
db= (char *)start; db= (char *)start;
query= (char *)(start + db_len + 1); query= (char *)(start + db_len + 1);
q_len= data_len - db_len -1; q_len= data_len - db_len -1;
if (data_len && (data_len < db_len ||
data_len < q_len ||
data_len != (db_len + q_len + 1)))
{
q_len= 0;
query= NULL;
DBUG_VOID_RETURN;
}
unsigned int max_length;
max_length= (event_len - ((const char*)(end + db_len + 1) -
(buf - common_header_len)));
if (q_len != max_length)
{
q_len= 0;
query= NULL;
DBUG_VOID_RETURN;
}
/** /**
Append the db length at the end of the buffer. This will be used by Append the db length at the end of the buffer. This will be used by
Query_cache::send_result_to_client() in case the query cache is On. 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, ...@@ -3278,6 +3297,26 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
you. you.
*/ */
thd->catalog= catalog_len ? (char *) catalog : (char *)""; thd->catalog= catalog_len ? (char *) catalog : (char *)"";
size_t valid_len;
bool len_error;
bool is_invalid_db_name= validate_string(system_charset_info, db, db_len,
&valid_len, &len_error);
DBUG_PRINT("debug",("is_invalid_db_name= %s, valid_len=%zu, len_error=%s",
is_invalid_db_name ? "true" : "false",
valid_len,
len_error ? "true" : "false"));
if (is_invalid_db_name || len_error)
{
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
"Invalid database name in Query event.");
thd->is_slave_error= true;
goto end;
}
new_db.length= db_len; new_db.length= db_len;
new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length); new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
thd->set_db(new_db.str, new_db.length); /* allocates a copy of 'db' */ 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, ...@@ -3454,7 +3493,23 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
} }
else else
thd->variables.collation_database= thd->db_charset; thd->variables.collation_database= thd->db_charset;
{
const CHARSET_INFO *cs= thd->charset();
/*
We cannot ask for parsing a statement using a character set
without state_maps (parser internal data).
*/
if (!cs->state_map)
{
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
"character_set cannot be parsed");
thd->is_slave_error= true;
goto end;
}
}
thd->table_map_for_update= (table_map)table_map_for_update; thd->table_map_for_update= (table_map)table_map_for_update;
thd->set_invoker(&user, &host); thd->set_invoker(&user, &host);
/* /*
...@@ -3898,7 +3953,13 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli) ...@@ -3898,7 +3953,13 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
*/ */
break; break;
default: default:
/* this case is impossible */ /*
This case is not expected. It can be either an event corruption or an
unsupported binary log version.
*/
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
"Binlog version not supported");
DBUG_RETURN(1); DBUG_RETURN(1);
} }
DBUG_RETURN(error); DBUG_RETURN(error);
...@@ -4724,6 +4785,9 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len, ...@@ -4724,6 +4785,9 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
fields = (char*)field_lens + num_fields; fields = (char*)field_lens + num_fields;
table_name = fields + field_block_len; table_name = fields + field_block_len;
if (strlen(table_name) > NAME_LEN)
goto err;
db = table_name + table_name_len + 1; db = table_name + table_name_len + 1;
DBUG_EXECUTE_IF ("simulate_invalid_address", DBUG_EXECUTE_IF ("simulate_invalid_address",
db_len = data_len;); db_len = data_len;);
...@@ -5889,6 +5953,13 @@ User_var_log_event(const char* buf, uint event_len, ...@@ -5889,6 +5953,13 @@ User_var_log_event(const char* buf, uint event_len,
buf+= description_event->common_header_len + buf+= description_event->common_header_len +
description_event->post_header_len[USER_VAR_EVENT-1]; description_event->post_header_len[USER_VAR_EVENT-1];
name_len= uint4korr(buf); name_len= uint4korr(buf);
/* Avoid reading out of buffer */
if ((buf - buf_start) + UV_NAME_LEN_SIZE + name_len > event_len)
{
error= true;
goto err;
}
name= (char *) buf + UV_NAME_LEN_SIZE; name= (char *) buf + UV_NAME_LEN_SIZE;
/* /*
...@@ -5948,8 +6019,11 @@ User_var_log_event(const char* buf, uint event_len, ...@@ -5948,8 +6019,11 @@ User_var_log_event(const char* buf, uint event_len,
we keep the flags set to UNDEF_F. we keep the flags set to UNDEF_F.
*/ */
uint bytes_read= ((val + val_len) - start); uint bytes_read= ((val + val_len) - start);
DBUG_ASSERT(bytes_read==data_written || if (bytes_read > event_len)
bytes_read==(data_written-1)); {
error= true;
goto err;
}
if ((data_written - bytes_read) > 0) if ((data_written - bytes_read) > 0)
{ {
flags= (uint) *(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + 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) ...@@ -6165,7 +6239,12 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
} }
if (!(charset= get_charset(charset_number, MYF(MY_WME)))) if (!(charset= get_charset(charset_number, MYF(MY_WME))))
{
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
"Invalid character set for User var event");
return 1; return 1;
}
LEX_STRING user_var_name; LEX_STRING user_var_name;
user_var_name.str= name; user_var_name.str= name;
user_var_name.length= name_len; user_var_name.length= name_len;
...@@ -6186,12 +6265,26 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli) ...@@ -6186,12 +6265,26 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
{ {
switch (type) { switch (type) {
case REAL_RESULT: case REAL_RESULT:
if (val_len != 8)
{
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
"Invalid variable length at User var event");
return 1;
}
float8get(real_val, val); float8get(real_val, val);
it= new Item_float(real_val, 0); it= new Item_float(real_val, 0);
val= (char*) &real_val; // Pointer to value in native format val= (char*) &real_val; // Pointer to value in native format
val_len= 8; val_len= 8;
break; break;
case INT_RESULT: case INT_RESULT:
if (val_len != 8)
{
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
"Invalid variable length at User var event");
return 1;
}
int_val= (longlong) uint8korr(val); int_val= (longlong) uint8korr(val);
it= new Item_int(int_val); it= new Item_int(int_val);
val= (char*) &int_val; // Pointer to value in native format val= (char*) &int_val; // Pointer to value in native format
...@@ -6199,6 +6292,13 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli) ...@@ -6199,6 +6292,13 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
break; break;
case DECIMAL_RESULT: case DECIMAL_RESULT:
{ {
if (val_len < 3)
{
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
"Invalid variable length at User var event");
return 1;
}
Item_decimal *dec= new Item_decimal((uchar*) val+2, val[0], val[1]); Item_decimal *dec= new Item_decimal((uchar*) val+2, val[0], val[1]);
it= dec; it= dec;
val= (char *)dec->val_decimal(NULL); val= (char *)dec->val_decimal(NULL);
...@@ -7646,6 +7746,15 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, ...@@ -7646,6 +7746,15 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
m_width = net_field_length(&ptr_after_width); m_width = net_field_length(&ptr_after_width);
DBUG_PRINT("debug", ("m_width=%lu", m_width)); DBUG_PRINT("debug", ("m_width=%lu", m_width));
/* Avoid reading out of buffer */
if (static_cast<unsigned int>((ptr_after_width +
(m_width + 7) / 8) -
(uchar*)buf) > event_len)
{
m_cols.bitmap= NULL;
DBUG_VOID_RETURN;
}
/* if bitmap_init fails, catched in is_valid() */ /* if bitmap_init fails, catched in is_valid() */
if (likely(!bitmap_init(&m_cols, if (likely(!bitmap_init(&m_cols,
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, 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, ...@@ -7694,7 +7803,12 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
const uchar* const ptr_rows_data= (const uchar*) ptr_after_width; const uchar* const ptr_rows_data= (const uchar*) ptr_after_width;
size_t const data_size= event_len - (ptr_rows_data - (const uchar *) buf); size_t const read_size= ptr_rows_data - (const unsigned char *) buf;
if (read_size > event_len)
{
DBUG_VOID_RETURN;
}
size_t const data_size= event_len - read_size;
DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %lu", DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %lu",
m_table_id, m_flags, m_width, (ulong) data_size)); m_table_id, m_flags, m_width, (ulong) data_size));
......
/* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by 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, ...@@ -1357,6 +1357,15 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len,
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
m_width = net_field_length(&ptr_after_width); m_width = net_field_length(&ptr_after_width);
DBUG_PRINT("debug", ("m_width=%lu", m_width)); DBUG_PRINT("debug", ("m_width=%lu", m_width));
/* Avoid reading out of buffer */
if (static_cast<unsigned int>(m_width +
(ptr_after_width -
(const uchar *)buf)) > event_len)
{
m_cols.bitmap= NULL;
DBUG_VOID_RETURN;
}
/* if bitmap_init fails, catched in is_valid() */ /* if bitmap_init fails, catched in is_valid() */
if (likely(!bitmap_init(&m_cols, if (likely(!bitmap_init(&m_cols,
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
......
/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by 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, ...@@ -191,6 +191,11 @@ template <class T> T available_buffer(const char* buf_start,
const char* buf_current, const char* buf_current,
T buf_len) T buf_len)
{ {
/* Sanity check */
if (buf_current < buf_start ||
buf_len < static_cast<T>(buf_current - buf_start))
return static_cast<T>(0);
return buf_len - (buf_current - buf_start); return buf_len - (buf_current - buf_start);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment