Index: lib/Fuzzer/FuzzerDriver.cpp =================================================================== --- lib/Fuzzer/FuzzerDriver.cpp +++ lib/Fuzzer/FuzzerDriver.cpp @@ -246,6 +246,7 @@ if (Flags.sync_command) Options.SyncCommand = Flags.sync_command; Options.SyncTimeout = Flags.sync_timeout; + Options.SaveRuntimeSanitizerErrors = Flags.save_runtime_sanitizer_errors; Fuzzer F(USF, Options); if (Flags.apply_tokens) Index: lib/Fuzzer/FuzzerFlags.def =================================================================== --- lib/Fuzzer/FuzzerFlags.def +++ lib/Fuzzer/FuzzerFlags.def @@ -58,3 +58,6 @@ "\" \" " "to synchronize the test corpus.") FUZZER_FLAG_INT(sync_timeout, 600, "Minimum timeout between syncs.") +FUZZER_FLAG_INT(save_runtime_sanitizer_errors, 0, + "Save runtime sanitizer errors" + " For example from fsanitize=undefined") Index: lib/Fuzzer/FuzzerInternal.h =================================================================== --- lib/Fuzzer/FuzzerInternal.h +++ lib/Fuzzer/FuzzerInternal.h @@ -72,6 +72,7 @@ std::string OutputCorpus; std::string SyncCommand; std::vector Tokens; + bool SaveRuntimeSanitizerErrors = false; }; Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options); void AddToCorpus(const Unit &U) { Corpus.push_back(U); } @@ -97,6 +98,8 @@ Unit SubstituteTokens(const Unit &U) const; + static void StaticErrorReportCallback(const char *ErrorSummary); + private: void AlarmCallback(); void ExecuteCallback(const Unit &U); @@ -108,7 +111,7 @@ size_t RunOneMaximizeFullCoverageSet(const Unit &U); size_t RunOneMaximizeCoveragePairs(const Unit &U); void WriteToOutputCorpus(const Unit &U); - void WriteToCrash(const Unit &U, const char *Prefix); + void WriteToCrash(const Unit &U, const char *Prefix, const char* EventName); void PrintStats(const char *Where, size_t Cov, const char *End = "\n"); void PrintUnitInASCIIOrTokens(const Unit &U, const char *PrintAfter = ""); @@ -125,6 +128,9 @@ // Apply Idx-th trace-based mutation to U. void ApplyTraceBasedMutation(size_t Idx, Unit *U); + + void ErrorReportCallback(const char *ErrorSummary); + void SetDeathCallback(); static void StaticDeathCallback(); void DeathCallback(); @@ -164,4 +170,4 @@ UserCallback Callback; }; -}; // namespace fuzzer +} // namespace fuzzer Index: lib/Fuzzer/FuzzerLoop.cpp =================================================================== --- lib/Fuzzer/FuzzerLoop.cpp +++ lib/Fuzzer/FuzzerLoop.cpp @@ -13,6 +13,10 @@ #include #include +void __sanitizer_report_error_summary(const char *ErrorSummary) { + fuzzer::Fuzzer::StaticErrorReportCallback(ErrorSummary); +} + namespace fuzzer { // Only one Fuzzer per process. @@ -40,6 +44,22 @@ } } +void Fuzzer::StaticErrorReportCallback(const char* ErrorSummary) { + assert(F); + F->ErrorReportCallback(ErrorSummary); +} + +void Fuzzer::ErrorReportCallback(const char* ErrorSummary) { + fuzzer::Printf("%s\n", ErrorSummary); + + if (Options.SaveRuntimeSanitizerErrors) { + Printf("ERROR:\n"); + Print(CurrentUnit, "\n"); + PrintUnitInASCIIOrTokens(CurrentUnit, "\n"); + WriteToCrash(CurrentUnit, "error-", "ERROR"); + } +} + void Fuzzer::StaticDeathCallback() { assert(F); F->DeathCallback(); @@ -49,7 +69,7 @@ Printf("DEATH:\n"); Print(CurrentUnit, "\n"); PrintUnitInASCIIOrTokens(CurrentUnit, "\n"); - WriteToCrash(CurrentUnit, "crash-"); + WriteToCrash(CurrentUnit, "crash-", "CRASHED"); } void Fuzzer::StaticAlarmCallback() { @@ -70,7 +90,7 @@ Options.UnitTimeoutSec); Print(CurrentUnit, "\n"); PrintUnitInASCIIOrTokens(CurrentUnit, "\n"); - WriteToCrash(CurrentUnit, "timeout-"); + WriteToCrash(CurrentUnit, "timeout-", "TIMEOUT"); exit(1); } } @@ -248,11 +268,12 @@ Printf("Written to %s\n", Path.c_str()); } -void Fuzzer::WriteToCrash(const Unit &U, const char *Prefix) { +void Fuzzer::WriteToCrash(const Unit &U, const char *Prefix, const char* name) { std::string Path = Prefix + Hash(U); WriteToFile(U, Path); - Printf("CRASHED; file written to %s\nBase64: ", Path.c_str()); + Printf("%s; file written to %s\nBase64: ", name, Path.c_str()); PrintFileAsBase64(Path); + Printf("\n"); } void Fuzzer::SaveCorpus() { Index: lib/Fuzzer/test/CMakeLists.txt =================================================================== --- lib/Fuzzer/test/CMakeLists.txt +++ lib/Fuzzer/test/CMakeLists.txt @@ -2,7 +2,7 @@ # basic blocks and we'll fail to discover the targets. # Also enable the coverage instrumentation back (it is disabled # for the Fuzzer lib) -set(CMAKE_CXX_FLAGS_RELEASE "${LIBFUZZER_FLAGS_BASE} -O0 -fsanitize-coverage=edge,indirect-calls") +set(CMAKE_CXX_FLAGS_RELEASE "${LIBFUZZER_FLAGS_BASE} -O0 -fsanitize=undefined -fsanitize-coverage=edge,indirect-calls") set(DFSanTests DFSanMemcmpTest @@ -18,6 +18,7 @@ NullDerefTest SimpleTest TimeoutTest + RuntimeErrorTest ${DFSanTests} ) Index: lib/Fuzzer/test/RuntimeErrorTest.cpp =================================================================== --- /dev/null +++ lib/Fuzzer/test/RuntimeErrorTest.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +static volatile unsigned int Sink; + +extern "C" void LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && Data[0] == 'H') { + Sink = 1; + if (Size > 1 && Data[1] == 'i') { + Sink = 2; + if (Size > 2 && Data[2] == '!') { + int64_t i = 1; i <<= 72; + exit(0); + } + } + } +} + Index: lib/Fuzzer/test/fuzzer.test =================================================================== --- lib/Fuzzer/test/fuzzer.test +++ lib/Fuzzer/test/fuzzer.test @@ -4,11 +4,11 @@ RUN: not ./LLVMFuzzer-InfiniteTest -timeout=2 2>&1 | FileCheck %s --check-prefix=InfiniteTest InfiniteTest: ALARM: working on the last Unit for -InfiniteTest: CRASHED; file written to timeout +InfiniteTest: TIMEOUT; file written to timeout RUN: not ./LLVMFuzzer-TimeoutTest -timeout=5 2>&1 | FileCheck %s --check-prefix=TimeoutTest TimeoutTest: ALARM: working on the last Unit for -TimeoutTest: CRASHED; file written to timeout +TimeoutTest: TIMEOUT; file written to timeout RUN: not ./LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=NullDerefTest NullDerefTest: CRASHED; file written to crash- @@ -28,3 +28,5 @@ RUN: not ./LLVMFuzzer-UserSuppliedFuzzerTest -seed=1 -timeout=15 2>&1 | FileCheck %s +RUN: ./LLVMFuzzer-RuntimeErrorTest -save_runtime_sanitizer_errors=1 2>&1 | FileCheck %s --check-prefix=RuntimeErrorTest +RuntimeErrorTest: ERROR; file written to