From b95d5cdaa4c0231abe906d8ad3912fbdd2f685fc Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Mon, 18 Mar 2013 15:01:16 +0530 Subject: Bug#14771299 OUT-OF-BOUND READS WRITE IN MYSQLBINLOG Problem: ======= Found using AddressSanitizer testing. The mysqlbinlog utility may result in out-of-bound heap buffer reads and thus, undefined behaviour, when processing RBR events in the old (pre-5.1 GA) format. The following code in process_event() would only be correct if Rows_log_event was the base class for Write,Update,Delete_rows_log_event_old classes: case PRE_GA_WRITE_ROWS_EVENT: case PRE_GA_DELETE_ROWS_EVENT: case PRE_GA_UPDATE_ROWS_EVENT: ... Rows_log_event *e= (Rows_log_event*) ev; Table_map_log_event *ignored_map= print_event_info->m_table_map_ignored.get_table(e->get_table_id()); ... if (e->get_flags(Rows_log_event::STMT_END_F)) { ... } However, Rows_log_event is only the base class for the Write,Update_Delete_rows_event family of classes, but not for their *_old counterparts. So the above typecasts are incorrect for the old-format RBR events and may result (and do result according to AddressSanitizer reports) in reading memory outside of the previously allocated on heap buffer. Fix: === The above mentioned invalid type cast has been replaced with appropriate old counterpart. Note:The above mentioned issue is present only mysql-5.1 and 5.5. This is fixed in mysql-5.6 and above as part of Bug#55790. Hence few of the relevant changes of Bug#55790 are being back ported to fix the current issue. client/mysqlbinlog.cc: The above mentioned invalid type cast of using new event object to read old events, has been replaced with appropriate old counterpart. Note:The above mentioned issue is present only mysql-5.1 and 5.5. This is fixed in mysql-5.6 and above as part of Bug#55790. Hence few of the relevant changes of Bug#55790 are being back ported to fix the current issue. --- client/mysqlbinlog.cc | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) (limited to 'client/mysqlbinlog.cc') diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index c32f92ae149..8a8c7e98940 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 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 @@ -929,20 +929,37 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, case PRE_GA_DELETE_ROWS_EVENT: case PRE_GA_UPDATE_ROWS_EVENT: { - if (ev_type != TABLE_MAP_EVENT) + bool stmt_end= FALSE; + Table_map_log_event *ignored_map= NULL; + + if (ev_type == WRITE_ROWS_EVENT || + ev_type == DELETE_ROWS_EVENT || + ev_type == UPDATE_ROWS_EVENT) { - Rows_log_event *e= (Rows_log_event*) ev; - Table_map_log_event *ignored_map= - print_event_info->m_table_map_ignored.get_table(e->get_table_id()); - bool skip_event= (ignored_map != NULL); - - /* - end of statement check: - i) destroy/free ignored maps - ii) if skip event, flush cache now - */ - if (e->get_flags(Rows_log_event::STMT_END_F)) - { + Rows_log_event *new_ev= (Rows_log_event*) ev; + if (new_ev->get_flags(Rows_log_event::STMT_END_F)) + stmt_end= TRUE; + ignored_map= print_event_info->m_table_map_ignored.get_table(new_ev->get_table_id()); + } + else if (ev_type == PRE_GA_WRITE_ROWS_EVENT || + ev_type == PRE_GA_DELETE_ROWS_EVENT || + ev_type == PRE_GA_UPDATE_ROWS_EVENT) + { + Old_rows_log_event *old_ev= (Old_rows_log_event*) ev; + if (old_ev->get_flags(Rows_log_event::STMT_END_F)) + stmt_end= TRUE; + ignored_map= print_event_info->m_table_map_ignored.get_table(old_ev->get_table_id()); + } + + bool skip_event= (ignored_map != NULL); + /* + end of statement check: + i) destroy/free ignored maps + ii) if skip event, flush cache now + */ + if (stmt_end) + { + /* Now is safe to clear ignored map (clear_tables will also delete original table map events stored in the map). @@ -969,7 +986,6 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, /* skip the event check */ if (skip_event) goto end; - } /* These events must be printed in base64 format, if printed. base64 format requires a FD event to be safe, so if no FD -- cgit v1.2.1