From 228d5b1ce48c1d21268d00d752cd8c07af56a0d1 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Tue, 1 Mar 2016 22:19:21 +0000 Subject: [PATCH] [libFuzzer] add generic signal handlers so that libFuzzer can report at least something if ASan is not handlig the signals for us. Remove abort_on_timeout flag. llvm-svn: 262415 --- llvm/lib/Fuzzer/FuzzerDriver.cpp | 7 ++++- llvm/lib/Fuzzer/FuzzerFlags.def | 9 +++++- llvm/lib/Fuzzer/FuzzerInternal.h | 13 ++++++++- llvm/lib/Fuzzer/FuzzerLoop.cpp | 50 ++++++++++++++++++++++++-------- llvm/lib/Fuzzer/FuzzerUtil.cpp | 30 +++++++++++++++---- llvm/lib/Fuzzer/test/fuzzer-timeout.test | 1 - llvm/lib/Fuzzer/test/fuzzer.test | 5 ++++ 7 files changed, 94 insertions(+), 21 deletions(-) diff --git a/llvm/lib/Fuzzer/FuzzerDriver.cpp b/llvm/lib/Fuzzer/FuzzerDriver.cpp index 5741093..c72b60a 100644 --- a/llvm/lib/Fuzzer/FuzzerDriver.cpp +++ b/llvm/lib/Fuzzer/FuzzerDriver.cpp @@ -265,7 +265,6 @@ static int FuzzerDriver(const std::vector &Args, Options.Verbosity = Flags.verbosity; Options.MaxLen = Flags.max_len; Options.UnitTimeoutSec = Flags.timeout; - Options.AbortOnTimeout = Flags.abort_on_timeout; Options.TimeoutExitCode = Flags.timeout_exitcode; Options.MaxTotalTimeSec = Flags.max_total_time; Options.DoCrossOver = Flags.cross_over; @@ -322,6 +321,12 @@ static int FuzzerDriver(const std::vector &Args, // Timer if (Flags.timeout > 0) SetTimer(Flags.timeout / 2 + 1); + if (Flags.handle_segv) SetSigSegvHandler(); + if (Flags.handle_bus) SetSigBusHandler(); + if (Flags.handle_abrt) SetSigAbrtHandler(); + if (Flags.handle_ill) SetSigIllHandler(); + if (Flags.handle_fpe) SetSigFpeHandler(); + if (Flags.handle_int) SetSigIntHandler(); if (Flags.test_single_input) { RunOneTest(&F, Flags.test_single_input); diff --git a/llvm/lib/Fuzzer/FuzzerFlags.def b/llvm/lib/Fuzzer/FuzzerFlags.def index aed3e4e..81dad2b 100644 --- a/llvm/lib/Fuzzer/FuzzerFlags.def +++ b/llvm/lib/Fuzzer/FuzzerFlags.def @@ -29,9 +29,10 @@ FUZZER_FLAG_INT( timeout, 1200, "Timeout in seconds (if positive). " "If one unit runs more than this number of seconds the process will abort.") -FUZZER_FLAG_INT(abort_on_timeout, 0, "If positive, call abort on timeout.") FUZZER_FLAG_INT(timeout_exitcode, 77, "Unless abort_on_timeout is set, use this exitcode on timeout.") +FUZZER_FLAG_INT(error_exit_code, 77, "When libFuzzer's signal handlers are in " + "use exit with this exitcode after catching a deadly signal.") FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total " "time in seconds to run the fuzzer.") FUZZER_FLAG_INT(help, 0, "Print help.") @@ -76,3 +77,9 @@ FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.") FUZZER_FLAG_INT(print_new_cov_pcs, 0, "If 1, print out new covered pcs.") FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.") +FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.") +FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.") +FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.") +FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.") diff --git a/llvm/lib/Fuzzer/FuzzerInternal.h b/llvm/lib/Fuzzer/FuzzerInternal.h index 62a77f8..9b9a554 100644 --- a/llvm/lib/Fuzzer/FuzzerInternal.h +++ b/llvm/lib/Fuzzer/FuzzerInternal.h @@ -83,6 +83,12 @@ void PrintASCII(const Unit &U, const char *PrintAfter = ""); void PrintASCII(const Word &W, const char *PrintAfter = ""); std::string Hash(const Unit &U); void SetTimer(int Seconds); +void SetSigSegvHandler(); +void SetSigBusHandler(); +void SetSigAbrtHandler(); +void SetSigIllHandler(); +void SetSigFpeHandler(); +void SetSigIntHandler(); std::string Base64(const Unit &U); int ExecuteCommand(const std::string &Command); size_t GetPeakRSSMb(); @@ -270,8 +276,8 @@ public: int Verbosity = 1; int MaxLen = 0; int UnitTimeoutSec = 300; - bool AbortOnTimeout = false; int TimeoutExitCode = 77; + int ErrorExitCode = 77; int MaxTotalTimeSec = 0; bool DoCrossOver = true; int MutateDepth = 5; @@ -331,6 +337,8 @@ public: size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; } static void StaticAlarmCallback(); + static void StaticCrashSignalCallback(); + static void StaticInterruptCallback(); void ExecuteCallback(const uint8_t *Data, size_t Size); @@ -341,6 +349,8 @@ public: private: void AlarmCallback(); + void CrashCallback(); + void InterruptCallback(); void MutateAndTestOne(); void ReportNewCoverage(const Unit &U); bool RunOne(const uint8_t *Data, size_t Size); @@ -372,6 +382,7 @@ private: void SetDeathCallback(); static void StaticDeathCallback(); + void DumpCurrentUnit(const char *Prefix); void DeathCallback(); uint8_t *CurrentUnitData; diff --git a/llvm/lib/Fuzzer/FuzzerLoop.cpp b/llvm/lib/Fuzzer/FuzzerLoop.cpp index f2d24ce..732cd02 100644 --- a/llvm/lib/Fuzzer/FuzzerLoop.cpp +++ b/llvm/lib/Fuzzer/FuzzerLoop.cpp @@ -83,15 +83,19 @@ void Fuzzer::StaticDeathCallback() { F->DeathCallback(); } -void Fuzzer::DeathCallback() { - if (!CurrentUnitSize) return; - Printf("DEATH:\n"); +void Fuzzer::DumpCurrentUnit(const char *Prefix) { if (CurrentUnitSize <= kMaxUnitSizeToPrint) { PrintHexArray(CurrentUnitData, CurrentUnitSize, "\n"); PrintASCII(CurrentUnitData, CurrentUnitSize, "\n"); } WriteUnitToFileWithPrefix( - {CurrentUnitData, CurrentUnitData + CurrentUnitSize}, "crash-"); + {CurrentUnitData, CurrentUnitData + CurrentUnitSize}, Prefix); +} + +void Fuzzer::DeathCallback() { + if (!CurrentUnitSize) return; + Printf("DEATH:\n"); + DumpCurrentUnit("crash-"); PrintFinalStats(); } @@ -100,6 +104,35 @@ void Fuzzer::StaticAlarmCallback() { F->AlarmCallback(); } +void Fuzzer::StaticCrashSignalCallback() { + assert(F); + F->CrashCallback(); +} + +void Fuzzer::StaticInterruptCallback() { + assert(F); + F->InterruptCallback(); +} + +void Fuzzer::CrashCallback() { + Printf("==%d== ERROR: libFuzzer: deadly signal\n", GetPid()); + if (__sanitizer_print_stack_trace) + __sanitizer_print_stack_trace(); + Printf("NOTE: libFuzzer has rudimentary signal handlers.\n" + " Combine libFuzzer with AddressSanitizer or similar for better " + "crash reports.\n"); + Printf("SUMMARY: libFuzzer: deadly signal\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + exit(Options.ErrorExitCode); +} + +void Fuzzer::InterruptCallback() { + Printf("==%d== libFuzzer: run interrupted; exiting\n", GetPid()); + PrintFinalStats(); + exit(0); +} + void Fuzzer::AlarmCallback() { assert(Options.UnitTimeoutSec > 0); if (!CurrentUnitSize) @@ -114,20 +147,13 @@ void Fuzzer::AlarmCallback() { 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); - if (CurrentUnitSize <= kMaxUnitSizeToPrint) { - PrintHexArray(CurrentUnitData, CurrentUnitSize, "\n"); - PrintASCII(CurrentUnitData, CurrentUnitSize, "\n"); - } - WriteUnitToFileWithPrefix( - {CurrentUnitData, CurrentUnitData + CurrentUnitSize}, "timeout-"); + DumpCurrentUnit("timeout-"); Printf("==%d== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(), Seconds); if (__sanitizer_print_stack_trace) __sanitizer_print_stack_trace(); Printf("SUMMARY: libFuzzer: timeout\n"); PrintFinalStats(); - if (Options.AbortOnTimeout) - abort(); exit(Options.TimeoutExitCode); } } diff --git a/llvm/lib/Fuzzer/FuzzerUtil.cpp b/llvm/lib/Fuzzer/FuzzerUtil.cpp index 1d51cf9..9364955 100644 --- a/llvm/lib/Fuzzer/FuzzerUtil.cpp +++ b/llvm/lib/Fuzzer/FuzzerUtil.cpp @@ -71,17 +71,37 @@ static void AlarmHandler(int, siginfo_t *, void *) { Fuzzer::StaticAlarmCallback(); } +static void CrashHandler(int, siginfo_t *, void *) { + Fuzzer::StaticCrashSignalCallback(); +} + +static void InterruptHandler(int, siginfo_t *, void *) { + Fuzzer::StaticInterruptCallback(); +} + +static void SetSigaction(int signum, + void (*callback)(int, siginfo_t *, void *)) { + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = callback; + int Res = sigaction(signum, &sigact, 0); + assert(Res == 0); +} + void SetTimer(int Seconds) { struct itimerval T {{Seconds, 0}, {Seconds, 0}}; int Res = setitimer(ITIMER_REAL, &T, nullptr); assert(Res == 0); - struct sigaction sigact; - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_sigaction = AlarmHandler; - Res = sigaction(SIGALRM, &sigact, 0); - assert(Res == 0); + SetSigaction(SIGALRM, AlarmHandler); } +void SetSigSegvHandler() { SetSigaction(SIGSEGV, CrashHandler); } +void SetSigBusHandler() { SetSigaction(SIGBUS, CrashHandler); } +void SetSigAbrtHandler() { SetSigaction(SIGABRT, CrashHandler); } +void SetSigIllHandler() { SetSigaction(SIGILL, CrashHandler); } +void SetSigFpeHandler() { SetSigaction(SIGFPE, CrashHandler); } +void SetSigIntHandler() { SetSigaction(SIGINT, InterruptHandler); } + int NumberOfCpuCores() { FILE *F = popen("nproc", "r"); int N = 0; diff --git a/llvm/lib/Fuzzer/test/fuzzer-timeout.test b/llvm/lib/Fuzzer/test/fuzzer-timeout.test index 2de460d..1c08871 100644 --- a/llvm/lib/Fuzzer/test/fuzzer-timeout.test +++ b/llvm/lib/Fuzzer/test/fuzzer-timeout.test @@ -11,5 +11,4 @@ RUN: not LLVMFuzzer-TimeoutTest -timeout=1 -test_single_input=%S/hi.txt 2>&1 | F SingleInputTimeoutTest: ALARM: working on the last Unit for SingleInputTimeoutTest-NOT: Test unit written to ./timeout- -RUN: ASAN_OPTIONS=handle_abort=0 not --crash LLVMFuzzer-TimeoutTest -timeout=1 -abort_on_timeout=1 RUN: LLVMFuzzer-TimeoutTest -timeout=1 -timeout_exitcode=0 diff --git a/llvm/lib/Fuzzer/test/fuzzer.test b/llvm/lib/Fuzzer/test/fuzzer.test index b316337..0822995 100644 --- a/llvm/lib/Fuzzer/test/fuzzer.test +++ b/llvm/lib/Fuzzer/test/fuzzer.test @@ -15,6 +15,11 @@ NullDerefTestPrefix: Test unit written to ZZZcrash- RUN: not LLVMFuzzer-NullDerefTest -artifact_prefix=ZZZ -exact_artifact_path=FOOBAR 2>&1 | FileCheck %s --check-prefix=NullDerefTestExactPath NullDerefTestExactPath: Test unit written to FOOBAR +RUN: ASAN_OPTIONS=handle_segv=0 not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=LIBFUZZER_OWN_SEGV_HANDLER +LIBFUZZER_OWN_SEGV_HANDLER: == ERROR: libFuzzer: deadly signal +LIBFUZZER_OWN_SEGV_HANDLER: SUMMARY: libFuzzer: deadly signal +LIBFUZZER_OWN_SEGV_HANDLER: Test unit written to ./crash- + #not LLVMFuzzer-FullCoverageSetTest -timeout=15 -seed=1 -mutate_depth=2 -use_full_coverage_set=1 2>&1 | FileCheck %s RUN: not LLVMFuzzer-CounterTest -use_counters=1 -max_len=6 -seed=1 -timeout=15 2>&1 | FileCheck %s -- 2.7.4