Index: compiler-rt/lib/asan/asan_report.cc =================================================================== --- compiler-rt/lib/asan/asan_report.cc +++ compiler-rt/lib/asan/asan_report.cc @@ -134,6 +134,10 @@ } ~ScopedInErrorReport() { + if (!__sanitizer_acquire_crash_state()) { + asanThreadRegistry().Unlock(); + return; + } ASAN_ON_ERROR(); if (current_error_.IsValid()) current_error_.Print(); Index: compiler-rt/lib/fuzzer/FuzzerExtFunctions.def =================================================================== --- compiler-rt/lib/fuzzer/FuzzerExtFunctions.def +++ compiler-rt/lib/fuzzer/FuzzerExtFunctions.def @@ -29,6 +29,7 @@ EXT_FUNC(__lsan_enable, void, (), false); EXT_FUNC(__lsan_disable, void, (), false); EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false); +EXT_FUNC(__sanitizer_acquire_crash_state, bool, (), true); EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, (void (*malloc_hook)(const volatile void *, size_t), void (*free_hook)(const volatile void *)), Index: compiler-rt/lib/fuzzer/FuzzerLoop.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -228,6 +228,9 @@ } void Fuzzer::CrashCallback() { + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); if (EF->__sanitizer_print_stack_trace) EF->__sanitizer_print_stack_trace(); @@ -243,6 +246,9 @@ void Fuzzer::ExitCallback() { if (!RunningCB) return; // This exit did not come from the user callback + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid()); if (EF->__sanitizer_print_stack_trace) EF->__sanitizer_print_stack_trace(); @@ -282,6 +288,9 @@ if (Options.Verbosity >= 2) Printf("AlarmCallback %zd\n", Seconds); if (Seconds >= (size_t)Options.UnitTimeoutSec) { + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds); Printf(" and the timeout value is %d (use -timeout=N to change)\n", Options.UnitTimeoutSec); @@ -297,6 +306,9 @@ } void Fuzzer::RssLimitCallback() { + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; Printf( "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n", GetPid(), GetPeakRSSMb(), Options.RssLimitMb); Index: compiler-rt/lib/fuzzer/FuzzerTracePC.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerTracePC.cpp +++ compiler-rt/lib/fuzzer/FuzzerTracePC.cpp @@ -20,6 +20,7 @@ #include "FuzzerIO.h" #include "FuzzerUtil.h" #include "FuzzerValueBitMap.h" +#include #include // The coverage counters and PCs. @@ -375,6 +376,14 @@ } // namespace fuzzer extern "C" { +// Ensures at most one crash report is printed per input execution. +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +bool __sanitizer_acquire_crash_state() { + static std::atomic InCrashState(false); + return !InCrashState.exchange(true, std::memory_order_relaxed); +} + ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_ALL void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { Index: compiler-rt/lib/sanitizer_common/sanitizer_common.cc =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_common.cc +++ compiler-rt/lib/sanitizer_common/sanitizer_common.cc @@ -343,6 +343,10 @@ Printf("%s\n", error_summary); } +SANITIZER_INTERFACE_WEAK_DEF(bool, __sanitizer_acquire_crash_state, void) { + return true; +} + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_set_death_callback(void (*callback)(void)) { SetUserDieCallback(callback); Index: compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc +++ compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc @@ -14,6 +14,7 @@ INTERFACE_FUNCTION(__sanitizer_set_report_path) INTERFACE_FUNCTION(__sanitizer_set_report_fd) INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container) +INTERFACE_WEAK_FUNCTION(__sanitizer_acquire_crash_state) INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary) INTERFACE_WEAK_FUNCTION(__sanitizer_sandbox_on_notify) // Sanitizer weak hooks Index: compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h +++ compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h @@ -46,6 +46,12 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_report_error_summary(const char *error_summary); + // This function is called by the tool before printing a crash report. If + // this function returns true, the crash report is printed, otherwise it is + // not. This function can be overridden by the client. + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool + __sanitizer_acquire_crash_state(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( const __sanitizer::uptr *pcs, const __sanitizer::uptr len); Index: compiler-rt/lib/sanitizer_common/weak_symbols.txt =================================================================== --- compiler-rt/lib/sanitizer_common/weak_symbols.txt +++ compiler-rt/lib/sanitizer_common/weak_symbols.txt @@ -1,3 +1,4 @@ +___sanitizer_acquire_crash_state ___sanitizer_free_hook ___sanitizer_malloc_hook ___sanitizer_report_error_summary Index: compiler-rt/test/fuzzer/AcquireCrashStateTest.cpp =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/AcquireCrashStateTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Ensures that error reports are suppressed after +// __sanitizer_acquire_crash_state() has been called the first time. +#include +#include +#include + +extern "C" bool __sanitizer_acquire_crash_state(); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + if (Size == 0) return 0; + __sanitizer_acquire_crash_state(); + exit(0); // No report should be generated here. +} + Index: compiler-rt/test/fuzzer/acquire-crash-state.test =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/acquire-crash-state.test @@ -0,0 +1,3 @@ +RUN: %cpp_compiler %S/AcquireCrashStateTest.cpp -o %t +RUN: %t 2>&1 | FileCheck %s +CHECK-NOT: fuzz target exited