diff --git a/drmemory/docs/fuzzer.dox b/drmemory/docs/fuzzer.dox old mode 100644 new mode 100755 index ec7707e7..29c62fba --- a/drmemory/docs/fuzzer.dox +++ b/drmemory/docs/fuzzer.dox @@ -177,6 +177,29 @@ you would specify the following command-line option to \p drmemory: -fuzz_corpus /path/to/inputs -fuzz_corpus_out /path/to/min_corpus/ +\section sec_per_iter_leak_scan Per-Iteration Leak Scanning + +Dr. Memory's fuzz mode typically performs a leak scan only at the end of the entire +fuzz run or on request. This option allows you to perform leak detection after every +target function execution. + + - \p -fuzz_per_iter_leak_scan: whether to run a leak scan after every + target function execution. + +Enabling this option is useful for precisely isolating the input iteration that +causes a memory leak, which aids in reproduction and minimization. The outputs +of these leak scans are stored in the fuzz_results.txt file. The types of leaks reported +are additionally controlled by the \p -possible_leaks and \p -show_reachable options. + +Note that the leak scan results include all leaks found to date, which will overlap with +leaks found in prior iterations. The user must currently infer manually which leaks +are newly found in the displayed iteration. + +For example, to fuzz the function \p DrMemFuzzFunc with per-iteration leak scanning enabled, +you would specify the following command-line option to \p drmemory: + + -fuzz_module a.out -fuzz_function DrMemFuzzFunc -fuzz_per_iter_leak_scan + **************************************************************************** **************************************************************************** */ diff --git a/drmemory/drmemory.c b/drmemory/drmemory.c old mode 100644 new mode 100755 index 4b278390..d5ee8e47 --- a/drmemory/drmemory.c +++ b/drmemory/drmemory.c @@ -86,6 +86,7 @@ file_t f_results = INVALID_FILE; file_t f_missing_symbols; file_t f_suppress; file_t f_potential; +file_t f_fuzz; static uint num_threads; #if defined(__DATE__) && defined(__TIME__) @@ -488,6 +489,8 @@ event_exit(void) close_file(f_missing_symbols); close_file(f_suppress); close_file(f_potential); + if (options.fuzz) // If it was even created + close_file(f_fuzz); dr_fprintf(f_global, "LOG END\n"); close_file(f_global); @@ -1508,6 +1511,8 @@ create_global_logfile(void) f_suppress = open_logfile("suppress.txt", false, -1); f_potential = open_logfile(RESULTS_POTENTIAL_FNAME, false, -1); print_version(f_potential, true); + if (options.fuzz) + f_fuzz = open_logfile(FUZZ_FNAME, false, -1); } } diff --git a/drmemory/drmemory.h b/drmemory/drmemory.h old mode 100644 new mode 100755 index 869e401c..cb181281 --- a/drmemory/drmemory.h +++ b/drmemory/drmemory.h @@ -74,6 +74,7 @@ extern char logsubdir[MAXIMUM_PATH]; #define RESULTS_FNAME "results.txt" #define RESULTS_POTENTIAL_FNAME "potential_errors.txt" +#define FUZZ_FNAME "fuzz_results.txt" #define POTENTIAL_PREFIX "potential" #define POTENTIAL_PREFIX_CAP "Potential" #define POTENTIAL_PREFIX_ALLCAP "POTENTIAL" @@ -82,6 +83,7 @@ extern file_t f_results; extern file_t f_suppress; extern file_t f_missing_symbols; extern file_t f_potential; +extern file_t f_fuzz; #ifdef WINDOWS extern app_pc ntdll_base; diff --git a/drmemory/fuzzer.c b/drmemory/fuzzer.c old mode 100644 new mode 100755 index 64f813b0..9b1c4567 --- a/drmemory/fuzzer.c +++ b/drmemory/fuzzer.c @@ -30,6 +30,7 @@ #include "drx.h" #include "drfuzz_mutator.h" #include "fuzzer.h" +#include "alloc_drmem.h" #include "drmemory.h" #include "drvector.h" #include "alloc.h" @@ -1603,6 +1604,24 @@ post_fuzz(void *fuzzcxt, generic_func_t target_pc) LOG(2, LOG_PREFIX" executing post-fuzz for "PIFX"\n", target_pc); + if (option_specified.fuzz_per_iter_leak_scan) { + ELOGF(0, f_fuzz, NL"[Thread (#%d) Iteration (#%d)]: Report for inputs (%s, %d):"NL"==========================================================================="NL, + fuzz_target.tid, fuzz_state->repeat_index + 1, fuzz_state->input_buffer, fuzz_state->input_size); + + /* XXX i#1797: Remove leaks seen in prior iterations to only display new leaks found in this iteration */ + report_leak_stats_checkpoint(); + check_reachability(false/*!at exit*/); + + ELOGF(0, f_fuzz, NL" LEAKS:"NL); + report_all_leak_stats(f_fuzz, false, false); + ELOGF(0, f_fuzz, NL" POTENTIAL LEAKS:"NL); + report_all_leak_stats(f_fuzz, false, true); + + report_leak_stats_revert(); + + ELOGF(0, f_fuzz, NL"==========================================================================="NL); + } + if (option_specified.fuzz_corpus) return post_fuzz_corpus(fuzzcxt, target_pc); diff --git a/drmemory/options.c b/drmemory/options.c old mode 100644 new mode 100755 index 52796a7c..5890c022 --- a/drmemory/options.c +++ b/drmemory/options.c @@ -580,7 +580,8 @@ options_init(const char *opstr) option_specified.fuzz_buffer_offset || option_specified.fuzz_skip_initial || IF_WINDOWS(option_specified.fuzz_mangled_names ||) - option_specified.fuzz_stat_freq) { + option_specified.fuzz_stat_freq || + option_specified.fuzz_per_iter_leak_scan) { options.fuzz = true; /* enable replace_buffer by default if fuzzing with input files */ if ((option_specified.fuzz_corpus || option_specified.fuzz_input_file) && diff --git a/drmemory/optionsx.h b/drmemory/optionsx.h old mode 100644 new mode 100755 index c56f982c..96c59d1d --- a/drmemory/optionsx.h +++ b/drmemory/optionsx.h @@ -746,6 +746,10 @@ OPTION_CLIENT_SCOPE(drmemscope, fuzz_skip_initial, uint, 0, 0, UINT_MAX, OPTION_CLIENT_SCOPE(drmemscope, fuzz_stat_freq, uint, 0, 0, UINT_MAX, "Enable fuzzer status logging with the specified frequency", "Specify the fuzzer status log frequency in number of fuzz iterations (no status is logged when this option is not set).") +OPTION_CLIENT_BOOL(drmemscope, fuzz_per_iter_leak_scan, false, + "Saves the fuzz input that triggered a leak to the current log directory.", + "Saves the fuzz input that triggered a leak to the current log directory. The name of the file is included in the error report summary.") + #ifdef WINDOWS OPTION_CLIENT_BOOL(drmemscope, fuzz_mangled_names, false, "Enable mangled names for fuzz targets on Windows", diff --git a/drmemory/report.c b/drmemory/report.c old mode 100644 new mode 100755 index 18760fd6..e9994443 --- a/drmemory/report.c +++ b/drmemory/report.c @@ -1586,6 +1586,8 @@ report_init(void) #endif ELOGF(0, f_suppress, "# File for suppressing errors found in pid %d: \"%s\""NL NL, dr_get_process_id(), dr_get_application_name()); + ELOGF(0, f_fuzz, "Dr. Memory fuzzing leaks for pid %d (\"%s\")"NL, + dr_get_process_id(), dr_get_application_name()); ELOGF(0, f_potential, "Dr. Memory errors that are likely to be false positives, " "for pid %d: \"%s\""NL, dr_get_process_id(), dr_get_application_name()); if ((options.lib_allowlist_frames > 0 && options.lib_allowlist[0] != '\0') || @@ -1902,10 +1904,27 @@ report_summary_to_file(file_t f, bool stderr_too, bool print_full_stats, bool po num_throttled_leaks); } } + + NOTIFY_COND(notify && options.fuzz, f, "Fuzz details: %s%c%s"NL, + logsubdir, DIRSEP, FUZZ_FNAME); + NOTIFY_COND(notify, f, "Details: %s%c%s"NL, logsubdir, DIRSEP, potential ? RESULTS_POTENTIAL_FNAME : RESULTS_FNAME); } +void +report_all_leak_stats(file_t f, bool notify, bool potential) { + report_leak_stats(f, notify, potential, ERROR_LEAK); + + if (options.possible_leaks) { + report_leak_stats(f, notify, potential, ERROR_POSSIBLE_LEAK); + } + + if (options.show_reachable) { + report_leak_stats(f, notify, potential, ERROR_REACHABLE_LEAK); + } +} + void report_summary(void) { @@ -3445,7 +3464,7 @@ report_leak(bool known_malloc, app_pc addr, size_t size, size_t indirect_size, } else { /* num_unique was set to 0 after nudge */ #ifdef STATISTICS /* for num_nudges */ - ASSERT(err->id == 0 || num_nudges > 0 || + ASSERT(err->id == 0 || num_nudges > 0 || option_specified.fuzz_per_iter_leak_scan || (maybe_reachable && !options.possible_leaks) || (reachable && !options.show_reachable), "invalid dup error report!"); diff --git a/drmemory/report.h b/drmemory/report.h old mode 100644 new mode 100755 index 910fc82b..3a4f721d --- a/drmemory/report.h +++ b/drmemory/report.h @@ -51,6 +51,9 @@ report_fork_init(void); void report_summary(void); +void +report_all_leak_stats(file_t f, bool notify, bool potential); + void report_thread_init(void *drcontext); diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt old mode 100644 new mode 100755 index daa5542c..f5a63805 --- a/tests/fuzz/CMakeLists.txt +++ b/tests/fuzz/CMakeLists.txt @@ -138,6 +138,11 @@ newtest_ex(fuzz_buffer.cpp fuzz_buffer.cpp "initialize" "" OFF "" 0) set(fuzz_buffer_drmem_ops "-no_fuzz_dump_on_error;-fuzz_target;${fuzz_buffer_symbol}|3|1|2|10${cpp_callconv}${enable_mangled_names}") +set(fuzz_iter_leak_base_ops + "-no_fuzz_dump_on_error;-fuzz_target;
!repeatme|2|0|1|10;-fuzz_per_iter_leak_scan") +set(fuzz_buffer.leak_iter.resmark "Fuzz details:") +newtest_nobuild_ex(fuzz_buffer.leak_iter fuzz_buffer + "initialize;leak" "${fuzz_iter_leak_base_ops}" "" OFF "" 0 "") if (NOT X64) # test detection of errors that requires shadow-memory newtest_nobuild_ex(fuzz_buffer.uninitialized.cpp fuzz_buffer.cpp "" "${fuzz_buffer_drmem_ops}" "" OFF "" 0 "") diff --git a/tests/fuzz/fuzz_buffer.leak_iter.out b/tests/fuzz/fuzz_buffer.leak_iter.out new file mode 100644 index 00000000..1f98cc78 --- /dev/null +++ b/tests/fuzz/fuzz_buffer.leak_iter.out @@ -0,0 +1,28 @@ +# ********************************************************** +# Copyright (c) 2015 Google, Inc. All rights reserved. +# ********************************************************** +# +# Dr. Memory: the memory debugger +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; +# version 2.1 of the License, and no later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +done +~~Dr.M~~ ERRORS FOUND: +~~Dr.M~~ 0 unique, 0 total unaddressable access(es) +~~Dr.M~~ 0 unique, 0 total uninitialized access(es) +~~Dr.M~~ 0 unique, 0 total invalid heap argument(s) +~~Dr.M~~ 0 unique, 0 total warning(s) +~~Dr.M~~ 0 unique, 1 total, 16 byte(s) of leak(s) +~~Dr.M~~ 0 unique, 0 total, 0 byte(s) of possible leak(s) diff --git a/tests/fuzz/fuzz_buffer.leak_iter.res b/tests/fuzz/fuzz_buffer.leak_iter.res new file mode 100755 index 00000000..7f61651d --- /dev/null +++ b/tests/fuzz/fuzz_buffer.leak_iter.res @@ -0,0 +1,34 @@ +# ********************************************************** +# Copyright (c) 2015 Google, Inc. All rights reserved. +# ********************************************************** +# +# Dr. Memory: the memory debugger +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; +# version 2.1 of the License, and no later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +Thread +Iteration +Report for inputs +=========================================================================== + + LEAKS: + 0 unique, 1 total, 16 byte(s) of leak(s) + 0 unique, 0 total, 0 byte(s) of possible leak(s) + + POTENTIAL LEAKS: + 1 unique, 0 total, 0 byte(s) of potential leak(s) + 0 unique, 0 total, 0 byte(s) of potential possible leak(s) + +===========================================================================