summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSujatha <sujatha.sivakumar@mariadb.com>2020-01-07 21:17:21 +0530
committerSujatha <sujatha.sivakumar@mariadb.com>2020-01-07 21:29:07 +0530
commit8317f77ccc490c94f2f43d2e6d4104c87f232563 (patch)
tree2d0368127f1b5fe80441fd02569e364c91f3121e
parent0b4ae6724f2707841c50f88d8c8598f2a0cdef4a (diff)
parentcb204e11eaf4c473ce5d5a10a21de147430057dc (diff)
downloadmariadb-git-8317f77ccc490c94f2f43d2e6d4104c87f232563.tar.gz
Merge branch '10.1' into 10.2
MDEV-18046: Assortment of crashes, assertion failures and ASAN errors in mysql_show_binlog_events Problem: ======== SHOW BINLOG EVENTS FROM <pos> reports following assert when ASAN is enabled. uint32 binlog_get_uncompress_len(const char*): Assertion `(buf[0] & 0xe0) == 0x80' failed Fix: === **Part11: Converted debug assert to error handler code**
-rw-r--r--mysql-test/suite/binlog/r/binlog_invalid_read_in_rotate.result18
-rw-r--r--mysql-test/suite/binlog/r/binlog_show_binlog_event_random_pos.result12
-rw-r--r--mysql-test/suite/binlog/t/binlog_invalid_read_in_rotate.combinations5
-rw-r--r--mysql-test/suite/binlog/t/binlog_invalid_read_in_rotate.test48
-rw-r--r--mysql-test/suite/binlog/t/binlog_show_binlog_event_random_pos.combinations5
-rw-r--r--mysql-test/suite/binlog/t/binlog_show_binlog_event_random_pos.test37
-rw-r--r--sql/log_event.cc92
-rw-r--r--sql/sql_repl.cc23
8 files changed, 209 insertions, 31 deletions
diff --git a/mysql-test/suite/binlog/r/binlog_invalid_read_in_rotate.result b/mysql-test/suite/binlog/r/binlog_invalid_read_in_rotate.result
new file mode 100644
index 00000000000..442fc951289
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_invalid_read_in_rotate.result
@@ -0,0 +1,18 @@
+RESET MASTER;
+call mtr.add_suppression("Error in Log_event::read_log_event*");
+call mtr.add_suppression("Replication event checksum verification failed while reading from a log file*");
+DROP TABLE /*! IF EXISTS*/ t1;
+Warnings:
+Note 1051 Unknown table 'test.t1'
+CREATE TABLE `t1` (
+`col_int` int,
+pk integer auto_increment,
+`col_char_12_key` char(12),
+`col_int_key` int,
+`col_char_12` char(12),
+primary key (pk),
+key (`col_char_12_key` ),
+key (`col_int_key` )) ENGINE=InnoDB;
+INSERT /*! IGNORE */ INTO t1 VALUES (183173120, NULL, 'abcd', 1, 'efghijk'), (1, NULL, 'lmno', 2, 'p');
+ALTER TABLE `t1` ENABLE KEYS;
+DROP TABLE t1;
diff --git a/mysql-test/suite/binlog/r/binlog_show_binlog_event_random_pos.result b/mysql-test/suite/binlog/r/binlog_show_binlog_event_random_pos.result
new file mode 100644
index 00000000000..358422c5842
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_show_binlog_event_random_pos.result
@@ -0,0 +1,12 @@
+RESET MASTER;
+call mtr.add_suppression("Error in Log_event::read_log_event*");
+call mtr.add_suppression("Replication event checksum verification failed while reading from a log file*");
+DROP TABLE IF EXISTS t1;
+Warnings:
+Note 1051 Unknown table 'test.t1'
+CREATE TABLE t1 (c1 CHAR(255), c2 CHAR(255), c3 CHAR(255), c4 CHAR(255), c5 CHAR(255));
+INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
+INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
+UPDATE t1 SET c1=repeat('b',255);
+INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
+DROP TABLE t1;
diff --git a/mysql-test/suite/binlog/t/binlog_invalid_read_in_rotate.combinations b/mysql-test/suite/binlog/t/binlog_invalid_read_in_rotate.combinations
new file mode 100644
index 00000000000..72076e12d48
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_invalid_read_in_rotate.combinations
@@ -0,0 +1,5 @@
+[enable_checksum]
+binlog_checksum=1
+
+[disable_checksum]
+binlog_checksum=0
diff --git a/mysql-test/suite/binlog/t/binlog_invalid_read_in_rotate.test b/mysql-test/suite/binlog/t/binlog_invalid_read_in_rotate.test
new file mode 100644
index 00000000000..36bb56c8342
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_invalid_read_in_rotate.test
@@ -0,0 +1,48 @@
+# ==== Purpose ====
+#
+# Test verifies that reading binary log by using "SHOW BINLOG EVENTS" command
+# from various random positions doesn't lead to crash. It should exit
+# gracefully with appropriate error.
+#
+# ==== References ====
+#
+# MDEV-18046:Assortment of crashes, assertion failures and ASAN errors in mysql_show_binlog_events
+
+--source include/have_log_bin.inc
+--source include/have_innodb.inc
+
+RESET MASTER;
+
+call mtr.add_suppression("Error in Log_event::read_log_event*");
+call mtr.add_suppression("Replication event checksum verification failed while reading from a log file*");
+
+DROP TABLE /*! IF EXISTS*/ t1;
+CREATE TABLE `t1` (
+`col_int` int,
+pk integer auto_increment,
+`col_char_12_key` char(12),
+`col_int_key` int,
+`col_char_12` char(12),
+primary key (pk),
+key (`col_char_12_key` ),
+key (`col_int_key` )) ENGINE=InnoDB;
+
+INSERT /*! IGNORE */ INTO t1 VALUES (183173120, NULL, 'abcd', 1, 'efghijk'), (1, NULL, 'lmno', 2, 'p');
+
+--disable_warnings
+ALTER TABLE `t1` ENABLE KEYS;
+--enable_warnings
+
+--let $max_pos= query_get_value(SHOW MASTER STATUS,Position,1)
+--let $pos= 1
+while ($pos <= $max_pos)
+{
+ --disable_query_log
+ --disable_result_log
+ --error 0,1220
+ eval SHOW BINLOG EVENTS FROM $pos;
+ --inc $pos
+ --enable_result_log
+ --enable_query_log
+}
+DROP TABLE t1;
diff --git a/mysql-test/suite/binlog/t/binlog_show_binlog_event_random_pos.combinations b/mysql-test/suite/binlog/t/binlog_show_binlog_event_random_pos.combinations
new file mode 100644
index 00000000000..72076e12d48
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_show_binlog_event_random_pos.combinations
@@ -0,0 +1,5 @@
+[enable_checksum]
+binlog_checksum=1
+
+[disable_checksum]
+binlog_checksum=0
diff --git a/mysql-test/suite/binlog/t/binlog_show_binlog_event_random_pos.test b/mysql-test/suite/binlog/t/binlog_show_binlog_event_random_pos.test
new file mode 100644
index 00000000000..e6a9e1cb2c1
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_show_binlog_event_random_pos.test
@@ -0,0 +1,37 @@
+# ==== Purpose ====
+#
+# Test verifies that reading binary log by using "SHOW BINLOG EVENTS" command
+# from various random positions doesn't lead to crash. It should exit
+# gracefully with appropriate error.
+#
+# ==== References ====
+#
+# MDEV-18046:Assortment of crashes, assertion failures and ASAN errors in mysql_show_binlog_events
+
+--source include/have_log_bin.inc
+--source include/have_innodb.inc
+
+RESET MASTER;
+call mtr.add_suppression("Error in Log_event::read_log_event*");
+call mtr.add_suppression("Replication event checksum verification failed while reading from a log file*");
+
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (c1 CHAR(255), c2 CHAR(255), c3 CHAR(255), c4 CHAR(255), c5 CHAR(255));
+INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
+INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
+UPDATE t1 SET c1=repeat('b',255);
+INSERT INTO t1 VALUES (repeat('a', 255), repeat('a', 255),repeat('a', 255),repeat('a', 255),repeat('a', 255));
+--let $max_pos= query_get_value(SHOW MASTER STATUS,Position,1)
+--let $pos= 1
+while ($pos <= $max_pos)
+{
+ --disable_query_log
+ --disable_result_log
+ --error 0,1220
+ eval SHOW BINLOG EVENTS FROM $pos LIMIT 100;
+ --inc $pos
+ --enable_result_log
+ --enable_query_log
+}
+
+DROP TABLE t1;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 66f1d6bc790..1778cfbcda5 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1040,9 +1040,14 @@ row_log_event_uncompress(const Format_description_log_event *description_event,
uint32 binlog_get_uncompress_len(const char *buf)
{
- DBUG_ASSERT((buf[0] & 0xe0) == 0x80);
- uint32 lenlen = buf[0] & 0x07;
uint32 len = 0;
+ uint32 lenlen = 0;
+
+ if ((buf == NULL) || ((buf[0] & 0xe0) != 0x80))
+ return len;
+
+ lenlen = buf[0] & 0x07;
+
switch(lenlen)
{
case 1:
@@ -4255,6 +4260,18 @@ code_name(int code)
}
#endif
+#define VALIDATE_BYTES_READ(CUR_POS, START, EVENT_LEN) \
+ do { \
+ uchar *cur_pos= (uchar *)CUR_POS; \
+ uchar *start= (uchar *)START; \
+ uint len= EVENT_LEN; \
+ uint bytes_read= (uint)(cur_pos - start); \
+ DBUG_PRINT("info", ("Bytes read: %u event_len:%u.\n",\
+ bytes_read, len)); \
+ if (bytes_read >= len) \
+ DBUG_VOID_RETURN; \
+ } while (0)
+
/**
Macro to check that there is enough space to read from memory.
@@ -4265,7 +4282,6 @@ code_name(int code)
#define CHECK_SPACE(PTR,END,CNT) \
do { \
DBUG_PRINT("info", ("Read %s", code_name(pos[-1]))); \
- DBUG_ASSERT((PTR) + (CNT) <= (END)); \
if ((PTR) + (CNT) > (END)) { \
DBUG_PRINT("info", ("query= 0")); \
query= 0; \
@@ -4578,7 +4594,9 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
uint32 max_length= uint32(event_len - ((const char*)(end + db_len + 1) -
(buf - common_header_len)));
- if (q_len != max_length)
+ if (q_len != max_length ||
+ (event_len < uint((const char*)(end + db_len + 1) -
+ (buf - common_header_len))))
{
q_len= 0;
query= NULL;
@@ -6713,6 +6731,8 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
{
DBUG_ENTER("Load_log_event::copy_log_event");
uint data_len;
+ if ((int) event_len < body_offset)
+ DBUG_RETURN(1);
char* buf_end = (char*)buf + event_len;
/* this is the beginning of the post-header */
const char* data_head = buf + description_event->common_header_len;
@@ -6722,9 +6742,7 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
table_name_len = (uint)data_head[L_TBL_LEN_OFFSET];
db_len = (uint)data_head[L_DB_LEN_OFFSET];
num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET);
-
- if ((int) event_len < body_offset)
- DBUG_RETURN(1);
+
/*
Sql_ex.init() on success returns the pointer to the first byte after
the sql_ex structure, which is the start of field lengths array.
@@ -6733,7 +6751,7 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
buf_end,
(uchar)buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
DBUG_RETURN(1);
-
+
data_len = event_len - body_offset;
if (num_fields > data_len) // simple sanity check against corruption
DBUG_RETURN(1);
@@ -7273,7 +7291,7 @@ Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
// The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
uint ident_offset;
- if (event_len < LOG_EVENT_MINIMAL_HEADER_LEN)
+ if (event_len < (uint)(LOG_EVENT_MINIMAL_HEADER_LEN + post_header_len))
DBUG_VOID_RETURN;
buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
pos= post_header_len ? uint8korr(buf + R_POS_OFFSET) : 4;
@@ -8783,6 +8801,11 @@ User_var_log_event(const char* buf, uint event_len,
we keep the flags set to UNDEF_F.
*/
size_t bytes_read= (val + val_len) - buf_start;
+ if (bytes_read > event_len)
+ {
+ error= true;
+ goto err;
+ }
if ((data_written - bytes_read) > 0)
{
flags= (uint) *(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
@@ -10346,7 +10369,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
uint8 const common_header_len= description_event->common_header_len;
Log_event_type event_type= (Log_event_type)(uchar)buf[EVENT_TYPE_OFFSET];
m_type= event_type;
-
+ m_cols_ai.bitmap= 0;
+
uint8 const post_header_len= description_event->post_header_len[event_type-1];
DBUG_PRINT("enter",("event_len: %u common_header_len: %d "
@@ -10380,7 +10404,14 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
which includes length bytes
*/
var_header_len= uint2korr(post_start);
- assert(var_header_len >= 2);
+ /* Check length and also avoid out of buffer read */
+ if (var_header_len < 2 ||
+ event_len < static_cast<unsigned int>(var_header_len +
+ (post_start - buf)))
+ {
+ m_cols.bitmap= 0;
+ DBUG_VOID_RETURN;
+ }
var_header_len-= 2;
/* Iterate over var-len header, extracting 'chunks' */
@@ -11942,14 +11973,12 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
m_data_size(0), m_field_metadata(0), m_field_metadata_size(0),
m_null_bits(0), m_meta_memory(NULL)
{
- unsigned int bytes_read= 0;
DBUG_ENTER("Table_map_log_event::Table_map_log_event(const char*,uint,...)");
uint8 common_header_len= description_event->common_header_len;
uint8 post_header_len= description_event->post_header_len[TABLE_MAP_EVENT-1];
DBUG_PRINT("info",("event_len: %u common_header_len: %d post_header_len: %d",
event_len, common_header_len, post_header_len));
-
/*
Don't print debug messages when running valgrind since they can
trigger false warnings.
@@ -11958,6 +11987,9 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
DBUG_DUMP("event buffer", (uchar*) buf, event_len);
#endif
+ if (event_len < (uint)(common_header_len + post_header_len))
+ DBUG_VOID_RETURN;
+
/* Read the post-header */
const char *post_start= buf + common_header_len;
@@ -11984,15 +12016,18 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
/* Extract the length of the various parts from the buffer */
uchar const *const ptr_dblen= (uchar const*)vpart + 0;
+ VALIDATE_BYTES_READ(ptr_dblen, buf, event_len);
m_dblen= *(uchar*) ptr_dblen;
/* Length of database name + counter + terminating null */
uchar const *const ptr_tbllen= ptr_dblen + m_dblen + 2;
+ VALIDATE_BYTES_READ(ptr_tbllen, buf, event_len);
m_tbllen= *(uchar*) ptr_tbllen;
/* Length of table name + counter + terminating null */
uchar const *const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
uchar *ptr_after_colcnt= (uchar*) ptr_colcnt;
+ VALIDATE_BYTES_READ(ptr_after_colcnt, buf, event_len);
m_colcnt= net_field_length(&ptr_after_colcnt);
DBUG_PRINT("info",("m_dblen: %lu off: %ld m_tbllen: %lu off: %ld m_colcnt: %lu off: %ld",
@@ -12015,23 +12050,27 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
memcpy(m_coltype, ptr_after_colcnt, m_colcnt);
ptr_after_colcnt= ptr_after_colcnt + m_colcnt;
- bytes_read= (uint) (ptr_after_colcnt - (uchar *)buf);
- DBUG_PRINT("info", ("Bytes read: %d.\n", bytes_read));
- if (bytes_read < event_len)
+ VALIDATE_BYTES_READ(ptr_after_colcnt, buf, event_len);
+ m_field_metadata_size= net_field_length(&ptr_after_colcnt);
+ if(m_field_metadata_size <= (m_colcnt * 2))
{
- m_field_metadata_size= net_field_length(&ptr_after_colcnt);
- DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));
uint num_null_bytes= (m_colcnt + 7) / 8;
m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
- &m_null_bits, num_null_bytes,
- &m_field_metadata, m_field_metadata_size,
- NULL);
+ &m_null_bits, num_null_bytes,
+ &m_field_metadata, m_field_metadata_size,
+ NULL);
memcpy(m_field_metadata, ptr_after_colcnt, m_field_metadata_size);
ptr_after_colcnt= (uchar*)ptr_after_colcnt + m_field_metadata_size;
memcpy(m_null_bits, ptr_after_colcnt, num_null_bytes);
}
+ else
+ {
+ m_coltype= NULL;
+ my_free(m_memory);
+ m_memory= NULL;
+ DBUG_VOID_RETURN;
+ }
}
-
DBUG_VOID_RETURN;
}
#endif
@@ -13797,9 +13836,12 @@ void Update_rows_log_event::init(MY_BITMAP const *cols)
Update_rows_log_event::~Update_rows_log_event()
{
- if (m_cols_ai.bitmap == m_bitbuf_ai) // no my_malloc happened
- m_cols_ai.bitmap= 0; // so no my_free in my_bitmap_free
- my_bitmap_free(&m_cols_ai); // To pair with my_bitmap_init().
+ if (m_cols_ai.bitmap)
+ {
+ if (m_cols_ai.bitmap == m_bitbuf_ai) // no my_malloc happened
+ m_cols_ai.bitmap= 0; // so no my_free in my_bitmap_free
+ my_bitmap_free(&m_cols_ai); // To pair with my_bitmap_init().
+ }
}
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 294dcc61713..a1ff4affd50 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -3860,6 +3860,11 @@ bool mysql_show_binlog_events(THD* thd)
List<Item> field_list;
const char *errmsg = 0;
bool ret = TRUE;
+ /*
+ Using checksum validate the correctness of event pos specified in show
+ binlog events command.
+ */
+ bool verify_checksum_once= false;
IO_CACHE log;
File file = -1;
MYSQL_BIN_LOG *binary_log= NULL;
@@ -3915,6 +3920,10 @@ bool mysql_show_binlog_events(THD* thd)
mi= 0;
}
+ /* Validate user given position using checksum */
+ if (lex_mi->pos == pos && !opt_master_verify_checksum)
+ verify_checksum_once= true;
+
unit->set_limit(thd->lex->current_select);
limit_start= unit->offset_limit_cnt;
limit_end= unit->select_limit_cnt;
@@ -3994,15 +4003,16 @@ bool mysql_show_binlog_events(THD* thd)
for (event_count = 0;
(ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0,
description_event,
- opt_master_verify_checksum)); )
+ (opt_master_verify_checksum ||
+ verify_checksum_once))); )
{
if (event_count >= limit_start &&
- ev->net_send(protocol, linfo.log_file_name, pos))
+ ev->net_send(protocol, linfo.log_file_name, pos))
{
- errmsg = "Net error";
- delete ev;
+ errmsg = "Net error";
+ delete ev;
mysql_mutex_unlock(log_lock);
- goto err;
+ goto err;
}
if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
@@ -4028,10 +4038,11 @@ bool mysql_show_binlog_events(THD* thd)
delete ev;
}
+ verify_checksum_once= false;
pos = my_b_tell(&log);
if (++event_count >= limit_end)
- break;
+ break;
}
if (event_count < limit_end && log.error)