Skip to content

Commit b0a5086

Browse files
author
Venkatesh Duggirala
committed
Bug#17632978 SLAVE CRASHES IF ROW EVENT IS CORRUPTED
(MYSQLBINLOG -V CRASHES WITH THAT BINLOG) Problem: If slave receives a corrupted row event, slave server is crashing. Analysis: When slave is unpacking the row event, it is not validating the data before applying the event. If the data is corrupted for eg: the length of a field is wrong, it could end up reading wrong data leading to a crash. A similar problem happens when mysqlbinlog tool is used against a corrupted binlog using '-v' option. Due to -v option, the tool tries to print the values of all the fields. Corrupted field length could lead to a crash. Fix: Before unpacking the field, a verification will be made on the length. If it falls into the event range, only then it will be unpacked. Otherwise, "ER_SLAVE_CORRUPT_EVENT" error will be thrown. Incase mysqlbinlog -v case, the field value will not be printed and the processing of the file will be stopped.
1 parent a5eccbc commit b0a5086

7 files changed

Lines changed: 41 additions & 45 deletions

File tree

sql/field.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef FIELD_INCLUDED
22
#define FIELD_INCLUDED
33

4-
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
4+
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
55
66
This program is free software; you can redistribute it and/or modify
77
it under the terms of the GNU General Public License as published by
@@ -1802,18 +1802,6 @@ class Field_blob :public Field_longstr {
18021802
{
18031803
store_length(ptr, packlength, number);
18041804
}
1805-
1806-
/**
1807-
Return the packed length plus the length of the data.
1808-
1809-
This is used to determine the size of the data plus the
1810-
packed length portion in the row data.
1811-
1812-
@returns The length in the row plus the size of the data.
1813-
*/
1814-
uint32 get_packed_size(const uchar *ptr_arg, bool low_byte_first)
1815-
{return packlength + get_length(ptr_arg, packlength, low_byte_first);}
1816-
18171805
inline uint32 get_length(uint row_offset= 0)
18181806
{ return get_length(ptr+row_offset, this->packlength, table->s->db_low_byte_first); }
18191807
uint32 get_length(const uchar *ptr, uint packlength, bool low_byte_first);

sql/log_event.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#ifdef MYSQL_CLIENT
1919

2020
#include "sql_priv.h"
21+
#include "mysqld_error.h"
2122

2223
#else
2324

@@ -1945,6 +1946,14 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
19451946
else
19461947
{
19471948
my_b_printf(file, "### @%d=", i + 1);
1949+
size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
1950+
if (value + fsize > m_rows_end)
1951+
{
1952+
my_b_printf(file, "***Corrupted replication event was detected."
1953+
" Not printing the value***\n");
1954+
value+= fsize;
1955+
return 0;
1956+
}
19481957
size_t size= log_event_print_value(file, value,
19491958
td->type(i), td->field_metadata(i),
19501959
typestr, sizeof(typestr));

sql/log_event.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3732,12 +3732,8 @@ class Rows_log_event : public Log_event
37323732
DBUG_ASSERT(m_table);
37333733

37343734
ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
3735-
int const result= ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
3736-
&m_curr_row_end, &m_master_reclength);
3737-
if (m_curr_row_end > m_rows_end)
3738-
my_error(ER_SLAVE_CORRUPT_EVENT, MYF(0));
3739-
ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);
3740-
return result;
3735+
return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
3736+
&m_curr_row_end, &m_master_reclength, m_rows_end);
37413737
}
37423738
#endif
37433739

sql/log_event_old.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,8 @@ class Old_rows_log_event : public Log_event
203203
{
204204
DBUG_ASSERT(m_table);
205205
ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
206-
int const result= ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
207-
&m_curr_row_end, &m_master_reclength);
208-
ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);
209-
return result;
206+
return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
207+
&m_curr_row_end, &m_master_reclength, m_rows_end);
210208
}
211209
#endif
212210

sql/rpl_record.cc

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2007, 2013, 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
@@ -169,11 +169,15 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
169169
@param row_data
170170
Packed row data
171171
@param cols Pointer to bitset describing columns to fill in
172-
@param row_end Pointer to variable that will hold the value of the
173-
one-after-end position for the row
172+
@param curr_row_end
173+
Pointer to variable that will hold the value of the
174+
one-after-end position for the current row
174175
@param master_reclength
175176
Pointer to variable that will be set to the length of the
176177
record on the master side
178+
@param row_end
179+
Pointer to variable that will hold the value of the
180+
end position for the data in the row event
177181
178182
@retval 0 No error
179183
@@ -185,7 +189,8 @@ int
185189
unpack_row(Relay_log_info const *rli,
186190
TABLE *table, uint const colcnt,
187191
uchar const *const row_data, MY_BITMAP const *cols,
188-
uchar const **const row_end, ulong *const master_reclength)
192+
uchar const **const current_row_end, ulong *const master_reclength,
193+
uchar const *const row_end)
189194
{
190195
DBUG_ENTER("unpack_row");
191196
DBUG_ASSERT(row_data);
@@ -303,6 +308,13 @@ unpack_row(Relay_log_info const *rli,
303308
#ifndef DBUG_OFF
304309
uchar const *const old_pack_ptr= pack_ptr;
305310
#endif
311+
uint32 len= tabledef->calc_field_size(i, (uchar *) pack_ptr);
312+
if ( pack_ptr + len > row_end )
313+
{
314+
pack_ptr+= len;
315+
my_error(ER_SLAVE_CORRUPT_EVENT, MYF(0));
316+
DBUG_RETURN(ER_SLAVE_CORRUPT_EVENT);
317+
}
306318
pack_ptr= f->unpack(f->ptr, pack_ptr, metadata, TRUE);
307319
DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;"
308320
" pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d",
@@ -369,6 +381,11 @@ unpack_row(Relay_log_info const *rli,
369381
uint32 len= tabledef->calc_field_size(i, (uchar *) pack_ptr);
370382
DBUG_DUMP("field_data", pack_ptr, len);
371383
pack_ptr+= len;
384+
if ( pack_ptr > row_end )
385+
{
386+
my_error(ER_SLAVE_CORRUPT_EVENT, MYF(0));
387+
DBUG_RETURN(ER_SLAVE_CORRUPT_EVENT);
388+
}
372389
}
373390
null_mask <<= 1;
374391
}
@@ -382,7 +399,7 @@ unpack_row(Relay_log_info const *rli,
382399

383400
DBUG_DUMP("row_data", row_data, pack_ptr - row_data);
384401

385-
*row_end = pack_ptr;
402+
*current_row_end = pack_ptr;
386403
if (master_reclength)
387404
{
388405
if (*field_ptr)

sql/rpl_record.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2007, 2013, 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
@@ -32,7 +32,8 @@ size_t pack_row(TABLE* table, MY_BITMAP const* cols,
3232
int unpack_row(Relay_log_info const *rli,
3333
TABLE *table, uint const colcnt,
3434
uchar const *const row_data, MY_BITMAP const *cols,
35-
uchar const **const row_end, ulong *const master_reclength);
35+
uchar const **const curr_row_end, ulong *const master_reclength,
36+
uchar const *const row_end);
3637

3738
// Fill table's record[0] with default values.
3839
int prepare_record(TABLE *const table, const uint skip, const bool check);

sql/rpl_utility.cc

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ int compare_lengths(Field *field, enum_field_types source_type, uint16 metadata)
186186
DBUG_PRINT("result", ("%d", result));
187187
DBUG_RETURN(result);
188188
}
189-
189+
#endif //MYSQL_CLIENT
190190
/*********************************************************************
191191
* table_def member definitions *
192192
*********************************************************************/
@@ -285,7 +285,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
285285
case MYSQL_TYPE_VARCHAR:
286286
{
287287
length= m_field_metadata[col] > 255 ? 2 : 1; // c&p of Field_varstring::data_length()
288-
DBUG_ASSERT(uint2korr(master_data) > 0);
289288
length+= length == 1 ? (uint32) *master_data : uint2korr(master_data);
290289
break;
291290
}
@@ -295,17 +294,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
295294
case MYSQL_TYPE_BLOB:
296295
case MYSQL_TYPE_GEOMETRY:
297296
{
298-
#if 1
299-
/*
300-
BUG#29549:
301-
This is currently broken for NDB, which is using big-endian
302-
order when packing length of BLOB. Once they have decided how to
303-
fix the issue, we can enable the code below to make sure to
304-
always read the length in little-endian order.
305-
*/
306-
Field_blob fb(m_field_metadata[col]);
307-
length= fb.get_packed_size(master_data, TRUE);
308-
#else
309297
/*
310298
Compute the length of the data. We cannot use get_length() here
311299
since it is dependent on the specific table (and also checks the
@@ -331,7 +319,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
331319
}
332320

333321
length+= m_field_metadata[col];
334-
#endif
335322
break;
336323
}
337324
default:
@@ -340,7 +327,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
340327
return length;
341328
}
342329

343-
330+
#ifndef MYSQL_CLIENT
344331
/**
345332
*/
346333
void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_INFO *field_cs)

0 commit comments

Comments
 (0)