summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorSujatha Sivakumar <sujatha.sivakumar@mariadb.com>2019-04-25 13:25:28 +0530
committerSujatha Sivakumar <sujatha.sivakumar@mariadb.com>2019-04-26 11:37:00 +0530
commit9a5a86f293b6fe40ad606de43c04a2d8ba6b60b1 (patch)
tree0e9cb696bfc953e4de241c356440edfe1a1845f6 /client
parent4e01bc8c963d9513625dd984cd1aca24b8a7b516 (diff)
downloadmariadb-git-9a5a86f293b6fe40ad606de43c04a2d8ba6b60b1.tar.gz
MDEV-17260: Memory leaks in mysqlbinlog
Problem: ======== The mysqlbinlog tool is leaking memory, causing failures in various tests when compiling and testing with AddressSanitizer or LeakSanitizer like this: cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_ASAN:BOOL=ON /path/to/source make -j$(nproc) cd mysql-test ASAN_OPTIONS=abort_on_error=1 ./mtr --parallel=auto Analysis: ========= Two types of leaks were observed during above execution. 1) Leak in Log_event::read_log_event(char const*, unsigned int, char const**, Format_description_log_event const*, char) File: sql/log_event.cc:2150 For all row based replication events the memory which is allocated during read_log_event is not freed after the event is processed. The event specific memory has to be retained only when flashback option is enabled with mysqlbinlog tool. In this case all the events are retained till the end statement is received and they are processed in reverse order and they are destroyed. But in the existing code all events are retained irrespective of flashback mode. Hence the memory leaks are observed. 2) read_remote_annotate_event(unsigned char*, unsigned long, char const**) File: client/mysqlbinlog.cc:194 In general the Annotate event is not printed immediately because all subsequent rbr-events can be filtered away. Instead it will be printed together with the first not filtered away Table map or the last rbr will be processed. While reading remote annotate events memory is allocated for event buffer and event's temp_buf is made to point to the allocated buffer as shown below. The TRUE flag is used for doing proper cleanup using free_temp_buf(). i.e at the time of deletion of annotate event its destructor takes care of clearing the temp_buf. /* Ensure the event->temp_buf is pointing to the allocated buffer. (TRUE = free temp_buf on the event deletion) */ event->register_temp_buf((char*)event_buf, TRUE); But existing code does the following when it receives a remote annotate_event. if (remote_opt) ev->temp_buf= 0; That is code immediately sets temp_buf=0, because of which free_temp_buf() call will return empty handed as it has lost the reference to the allocated temporary buffer. This results in memory leak Fix: ==== 1) If not in flashback mode, destroy the memory for events once they are processed. 2) Remove the ev->temp_buf=0 code for remote option. Let the proper cleanup to be done as part of free_temp_buf().
Diffstat (limited to 'client')
-rw-r--r--client/mysqlbinlog.cc9
1 files changed, 3 insertions, 6 deletions
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index cfc05cbf794..3dad4fef67b 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -227,8 +227,7 @@ void print_annotate_event(PRINT_EVENT_INFO *print_event_info)
if (annotate_event)
{
annotate_event->print(result_file, print_event_info);
- delete annotate_event; // the event should not be printed more than once
- annotate_event= 0;
+ free_annotate_event();
}
}
@@ -1465,7 +1464,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1478,7 +1477,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Old_rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1539,8 +1538,6 @@ end:
}
}
- if (remote_opt)
- ev->temp_buf= 0;
if (destroy_evt) /* destroy it later if not set (ignored table map) */
delete ev;
}