Skip to content

Commit 42ed860

Browse files
committed
[libFuzzer] Report at most one crash per input.
Summary: Fixes google/sanitizers#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 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@331310 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent dcd2491 commit 42ed860

File tree

9 files changed

+57
-0
lines changed

9 files changed

+57
-0
lines changed

include/sanitizer/common_interface_defs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ extern "C" {
6565
void __sanitizer_unaligned_store32(void *p, uint32_t x);
6666
void __sanitizer_unaligned_store64(void *p, uint64_t x);
6767

68+
// Returns 1 on the first call, then returns 0 thereafter. Called by the tool
69+
// to ensure only one report is printed when multiple errors occur
70+
// simultaneously.
71+
int __sanitizer_acquire_crash_state();
72+
6873
// Annotate the current state of a contiguous container, such as
6974
// std::vector, std::string or similar.
7075
// A contiguous container is a container that keeps all of its elements

lib/asan/asan_report.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ class ScopedInErrorReport {
134134
}
135135

136136
~ScopedInErrorReport() {
137+
if (halt_on_error_ && !__sanitizer_acquire_crash_state()) {
138+
asanThreadRegistry().Unlock();
139+
return;
140+
}
137141
ASAN_ON_ERROR();
138142
if (current_error_.IsValid()) current_error_.Print();
139143

lib/fuzzer/FuzzerExtFunctions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
2929
EXT_FUNC(__lsan_enable, void, (), false);
3030
EXT_FUNC(__lsan_disable, void, (), false);
3131
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
32+
EXT_FUNC(__sanitizer_acquire_crash_state, bool, (), true);
3233
EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
3334
(void (*malloc_hook)(const volatile void *, size_t),
3435
void (*free_hook)(const volatile void *)),

lib/fuzzer/FuzzerLoop.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ void Fuzzer::StaticFileSizeExceedCallback() {
228228
}
229229

230230
void Fuzzer::CrashCallback() {
231+
if (EF->__sanitizer_acquire_crash_state &&
232+
!EF->__sanitizer_acquire_crash_state())
233+
return;
231234
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
232235
if (EF->__sanitizer_print_stack_trace)
233236
EF->__sanitizer_print_stack_trace();
@@ -243,6 +246,9 @@ void Fuzzer::CrashCallback() {
243246
void Fuzzer::ExitCallback() {
244247
if (!RunningCB)
245248
return; // This exit did not come from the user callback
249+
if (EF->__sanitizer_acquire_crash_state &&
250+
!EF->__sanitizer_acquire_crash_state())
251+
return;
246252
Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid());
247253
if (EF->__sanitizer_print_stack_trace)
248254
EF->__sanitizer_print_stack_trace();
@@ -282,6 +288,9 @@ void Fuzzer::AlarmCallback() {
282288
if (Options.Verbosity >= 2)
283289
Printf("AlarmCallback %zd\n", Seconds);
284290
if (Seconds >= (size_t)Options.UnitTimeoutSec) {
291+
if (EF->__sanitizer_acquire_crash_state &&
292+
!EF->__sanitizer_acquire_crash_state())
293+
return;
285294
Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds);
286295
Printf(" and the timeout value is %d (use -timeout=N to change)\n",
287296
Options.UnitTimeoutSec);
@@ -297,6 +306,9 @@ void Fuzzer::AlarmCallback() {
297306
}
298307

299308
void Fuzzer::RssLimitCallback() {
309+
if (EF->__sanitizer_acquire_crash_state &&
310+
!EF->__sanitizer_acquire_crash_state())
311+
return;
300312
Printf(
301313
"==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n",
302314
GetPid(), GetPeakRSSMb(), Options.RssLimitMb);

lib/sanitizer_common/sanitizer_common.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "sanitizer_common.h"
1515
#include "sanitizer_allocator_interface.h"
1616
#include "sanitizer_allocator_internal.h"
17+
#include "sanitizer_atomic.h"
1718
#include "sanitizer_flags.h"
1819
#include "sanitizer_libc.h"
1920
#include "sanitizer_placement_new.h"
@@ -343,6 +344,12 @@ SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary,
343344
Printf("%s\n", error_summary);
344345
}
345346

347+
SANITIZER_INTERFACE_ATTRIBUTE
348+
int __sanitizer_acquire_crash_state() {
349+
static atomic_uint8_t in_crash_state = {};
350+
return !atomic_exchange(&in_crash_state, 1, memory_order_relaxed);
351+
}
352+
346353
SANITIZER_INTERFACE_ATTRIBUTE
347354
void __sanitizer_set_death_callback(void (*callback)(void)) {
348355
SetUserDieCallback(callback);

lib/sanitizer_common/sanitizer_common_interface.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//===----------------------------------------------------------------------===//
99
// Sanitizer Common interface list.
1010
//===----------------------------------------------------------------------===//
11+
INTERFACE_FUNCTION(__sanitizer_acquire_crash_state)
1112
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
1213
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
1314
INTERFACE_FUNCTION(__sanitizer_set_death_callback)

lib/sanitizer_common/sanitizer_interface_internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ extern "C" {
5252
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage();
5353

5454
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
55+
56+
// Returns 1 on the first call, then returns 0 thereafter. Called by the tool
57+
// to ensure only one report is printed when multiple errors occur
58+
// simultaneously.
59+
SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_acquire_crash_state();
60+
5561
SANITIZER_INTERFACE_ATTRIBUTE
5662
void __sanitizer_annotate_contiguous_container(const void *beg,
5763
const void *end,

test/fuzzer/AcquireCrashStateTest.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This file is distributed under the University of Illinois Open Source
2+
// License. See LICENSE.TXT for details.
3+
4+
// Ensures that error reports are suppressed after
5+
// __sanitizer_acquire_crash_state() has been called the first time.
6+
#include "sanitizer/common_interface_defs.h"
7+
8+
#include <cassert>
9+
#include <cstdint>
10+
#include <cstdlib>
11+
12+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
13+
assert(Data);
14+
if (Size == 0) return 0;
15+
__sanitizer_acquire_crash_state();
16+
exit(0); // No report should be generated here.
17+
}
18+

test/fuzzer/acquire-crash-state.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
RUN: %cpp_compiler %S/AcquireCrashStateTest.cpp -o %t
2+
RUN: %t 2>&1 | FileCheck %s
3+
CHECK-NOT: fuzz target exited

0 commit comments

Comments
 (0)