[libFuzzer] Report at most one crash per input.

Summary:
Fixes https://github.com/google/sanitizers/issues/788/, a deadlock
caused by multiple crashes happening at the same time.  Before printing
a crash report, we now test and set an atomic flag.  If the flag was
already set, the crash handler returns immediately.

Reviewers: kcc

Reviewed By: kcc

Subscribers: llvm-commits, kubamracek

Differential Revision: https://reviews.llvm.org/D46277

llvm-svn: 331310
This commit is contained in:
Matt Morehouse 2018-05-01 21:01:53 +00:00
parent f70671582d
commit 52fd169035
9 changed files with 57 additions and 0 deletions

View File

@ -65,6 +65,11 @@ extern "C" {
void __sanitizer_unaligned_store32(void *p, uint32_t x);
void __sanitizer_unaligned_store64(void *p, uint64_t x);
// Returns 1 on the first call, then returns 0 thereafter. Called by the tool
// to ensure only one report is printed when multiple errors occur
// simultaneously.
int __sanitizer_acquire_crash_state();
// Annotate the current state of a contiguous container, such as
// std::vector, std::string or similar.
// A contiguous container is a container that keeps all of its elements

View File

@ -134,6 +134,10 @@ class ScopedInErrorReport {
}
~ScopedInErrorReport() {
if (halt_on_error_ && !__sanitizer_acquire_crash_state()) {
asanThreadRegistry().Unlock();
return;
}
ASAN_ON_ERROR();
if (current_error_.IsValid()) current_error_.Print();

View File

@ -29,6 +29,7 @@ EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
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 *)),

View File

@ -228,6 +228,9 @@ void Fuzzer::StaticFileSizeExceedCallback() {
}
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::CrashCallback() {
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 @@ void Fuzzer::AlarmCallback() {
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::AlarmCallback() {
}
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);

View File

@ -14,6 +14,7 @@
#include "sanitizer_common.h"
#include "sanitizer_allocator_interface.h"
#include "sanitizer_allocator_internal.h"
#include "sanitizer_atomic.h"
#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
#include "sanitizer_placement_new.h"
@ -343,6 +344,12 @@ SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary,
Printf("%s\n", error_summary);
}
SANITIZER_INTERFACE_ATTRIBUTE
int __sanitizer_acquire_crash_state() {
static atomic_uint8_t in_crash_state = {};
return !atomic_exchange(&in_crash_state, 1, memory_order_relaxed);
}
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_set_death_callback(void (*callback)(void)) {
SetUserDieCallback(callback);

View File

@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
// Sanitizer Common interface list.
//===----------------------------------------------------------------------===//
INTERFACE_FUNCTION(__sanitizer_acquire_crash_state)
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
INTERFACE_FUNCTION(__sanitizer_set_death_callback)

View File

@ -52,6 +52,12 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage();
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
// Returns 1 on the first call, then returns 0 thereafter. Called by the tool
// to ensure only one report is printed when multiple errors occur
// simultaneously.
SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_acquire_crash_state();
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_annotate_contiguous_container(const void *beg,
const void *end,

View File

@ -0,0 +1,18 @@
// 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 "sanitizer/common_interface_defs.h"
#include <cassert>
#include <cstdint>
#include <cstdlib>
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.
}

View File

@ -0,0 +1,3 @@
RUN: %cpp_compiler %S/AcquireCrashStateTest.cpp -o %t
RUN: %t 2>&1 | FileCheck %s
CHECK-NOT: fuzz target exited