Index: compiler-rt/lib/fuzzer/CMakeLists.txt =================================================================== --- compiler-rt/lib/fuzzer/CMakeLists.txt +++ compiler-rt/lib/fuzzer/CMakeLists.txt @@ -40,6 +40,7 @@ FuzzerInterface.h FuzzerInternal.h FuzzerMerge.h + FuzzerMonitor.h FuzzerMutate.h FuzzerOptions.h FuzzerRandom.h Index: compiler-rt/lib/fuzzer/FuzzerDriver.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -15,6 +15,7 @@ #include "FuzzerInterface.h" #include "FuzzerInternal.h" #include "FuzzerMerge.h" +#include "FuzzerMonitor.h" #include "FuzzerMutate.h" #include "FuzzerPlatform.h" #include "FuzzerRandom.h" @@ -24,10 +25,10 @@ #include #include #include +#include #include #include #include -#include // This function should be present in the libFuzzer so that the client // binary can test for its existence. @@ -300,19 +301,19 @@ return HasErrors ? 1 : 0; } -static void RssThread(Fuzzer *F, size_t RssLimitMb) { +static void RssThread(size_t RssLimitMb) { while (true) { SleepSeconds(1); size_t Peak = GetPeakRSSMb(); if (Peak > RssLimitMb) - F->RssLimitCallback(); + FuzzerRssLimitCallback(0); } } -static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { +static void StartRssThread(size_t RssLimitMb) { if (!RssLimitMb) return; - std::thread T(RssThread, F, RssLimitMb); + std::thread T(RssThread, RssLimitMb); T.detach(); } @@ -815,7 +816,7 @@ // Threads are only supported by Chrome. Don't use them with emscripten // for now. #if !LIBFUZZER_EMSCRIPTEN - StartRssThread(F, Flags.rss_limit_mb); + StartRssThread(Flags.rss_limit_mb); #endif // LIBFUZZER_EMSCRIPTEN Options.HandleAbrt = Flags.handle_abrt; @@ -833,7 +834,7 @@ SetSignalHandler(Options); - std::atexit(Fuzzer::StaticExitCallback); + std::atexit([]() { FuzzerExitCallback(0); }); if (Flags.minimize_crash) return MinimizeCrashInput(Args, Options); Index: compiler-rt/lib/fuzzer/FuzzerInternal.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerInternal.h +++ compiler-rt/lib/fuzzer/FuzzerInternal.h @@ -58,12 +58,6 @@ size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; } - static void StaticAlarmCallback(); - static void StaticCrashSignalCallback(); - static void StaticExitCallback(); - static void StaticInterruptCallback(); - static void StaticFileSizeExceedCallback(); - static void StaticGracefulExitCallback(); void ExecuteCallback(const uint8_t *Data, size_t Size); bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, @@ -78,7 +72,6 @@ void PrintFinalStats(); void SetMaxInputLen(size_t MaxInputLen); void SetMaxMutationLen(size_t MaxMutationLen); - void RssLimitCallback(); bool InFuzzingThread() const { return IsMyThread; } size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const; @@ -89,25 +82,35 @@ static void MaybeExitGracefully(); std::string WriteToOutputCorpus(const Unit &U); -private: void AlarmCallback(); - void CrashCallback(); - void ExitCallback(); - void CrashOnOverwrittenData(); + void CrashCallback(unsigned long PID); + void DeathCallback(); + void ExitCallback(unsigned long PID); + void FileSizeExceedCallback(); + void GracefulExitCallback(); void InterruptCallback(); + void LeakCallback(unsigned long PID); + void OutOfMemoryCallback(unsigned long PID, size_t LastMalloc); + +private: + void CrashOnOverwrittenData(); void MutateAndTestOne(); void PurgeAllocator(); void ReportNewCoverage(InputInfo *II, const Unit &U); void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix); + + // For these, PID == 0 refers to the current process. + unsigned long GetPid(unsigned long PID = 0); + void PrintStackTrace(unsigned long PID = 0); + void PrintMemoryProfile(unsigned long PID = 0); + void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0, size_t Features = 0); void PrintStatusForNewUnit(const Unit &U, const char *Text); void CheckExitOnSrcPosOrItem(); - static void StaticDeathCallback(); void DumpCurrentUnit(const char *Prefix); - void DeathCallback(); void AllocateCurrentUnitData(); uint8_t *CurrentUnitData = nullptr; @@ -117,6 +120,7 @@ bool GracefulExitRequested = false; size_t TotalNumberOfRuns = 0; + bool ExecutingSeedCorpora = false; size_t NumberOfNewUnitsAdded = 0; size_t LastCorpusUpdateRun = 0; Index: compiler-rt/lib/fuzzer/FuzzerLoop.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -12,6 +12,7 @@ #include "FuzzerFork.h" #include "FuzzerIO.h" #include "FuzzerInternal.h" +#include "FuzzerMonitor.h" #include "FuzzerMutate.h" #include "FuzzerPlatform.h" #include "FuzzerRandom.h" @@ -140,7 +141,7 @@ FuzzingOptions Options) : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { if (EF->__sanitizer_set_death_callback) - EF->__sanitizer_set_death_callback(StaticDeathCallback); + EF->__sanitizer_set_death_callback(FuzzerDeathCallback); assert(!F); F = this; TPC.ResetMaps(); @@ -149,7 +150,6 @@ EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); TPC.SetUseCounters(Options.UseCounters); TPC.SetUseValueProfileMask(Options.UseValueProfile); - if (Options.Verbosity) TPC.PrintModuleInfo(); if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) @@ -169,11 +169,6 @@ CurrentUnitData = new uint8_t[MaxInputLen]; } -void Fuzzer::StaticDeathCallback() { - assert(F); - F->DeathCallback(); -} - void Fuzzer::DumpCurrentUnit(const char *Prefix) { if (!CurrentUnitData) return; // Happens when running individual inputs. @@ -195,43 +190,22 @@ PrintFinalStats(); } -void Fuzzer::StaticAlarmCallback() { - assert(F); - F->AlarmCallback(); -} - -void Fuzzer::StaticCrashSignalCallback() { - assert(F); - F->CrashCallback(); -} - -void Fuzzer::StaticExitCallback() { - assert(F); - F->ExitCallback(); -} - -void Fuzzer::StaticInterruptCallback() { - assert(F); - F->InterruptCallback(); -} - -void Fuzzer::StaticGracefulExitCallback() { - assert(F); - F->GracefulExitRequested = true; +void Fuzzer::GracefulExitCallback() { + GracefulExitRequested = true; Printf("INFO: signal received, trying to exit gracefully\n"); } -void Fuzzer::StaticFileSizeExceedCallback() { +void Fuzzer::FileSizeExceedCallback() { Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid()); exit(1); } -void Fuzzer::CrashCallback() { +void Fuzzer::CrashCallback(unsigned long PID) { if (EF->__sanitizer_acquire_crash_state && !EF->__sanitizer_acquire_crash_state()) return; - Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); - PrintStackTrace(); + Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid(PID)); + PrintStackTrace(PID); Printf("NOTE: libFuzzer has rudimentary signal handlers.\n" " Combine libFuzzer with AddressSanitizer or similar for better " "crash reports.\n"); @@ -241,14 +215,14 @@ _Exit(Options.ErrorExitCode); // Stop right now. } -void Fuzzer::ExitCallback() { +void Fuzzer::ExitCallback(unsigned long PID) { if (!RunningUserCallback) 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()); - PrintStackTrace(); + Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid(PID)); + PrintStackTrace(PID); Printf("SUMMARY: libFuzzer: fuzz target exited\n"); DumpCurrentUnit("crash-"); PrintFinalStats(); @@ -256,8 +230,9 @@ } void Fuzzer::MaybeExitGracefully() { - if (!F->GracefulExitRequested) return; - Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid()); + if (!F->GracefulExitRequested) + return; + Printf("==%lu== INFO: libFuzzer: exiting as requested\n", fuzzer::GetPid()); RmDirRecursive(TempPath("FuzzWithFork", ".dir")); F->PrintFinalStats(); _Exit(0); @@ -306,21 +281,46 @@ } } -void Fuzzer::RssLimitCallback() { +void Fuzzer::OutOfMemoryCallback(unsigned long PID, size_t LastMalloc) { 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); - Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); - PrintMemoryProfile(); + Printf("==%lu== ERROR: libFuzzer: out-of-memory ", GetPid(PID)); + if (LastMalloc) { + Printf("(malloc(%zd))\n", LastMalloc); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + PrintStackTrace(); + } else { + Printf("(used: %zdMb; limit: %zdMb)\n", GetPeakRSSMb(), Options.RssLimitMb); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + PrintMemoryProfile(PID); + } DumpCurrentUnit("oom-"); Printf("SUMMARY: libFuzzer: out-of-memory\n"); PrintFinalStats(); _Exit(Options.OOMExitCode); // Stop right now. } +void Fuzzer::LeakCallback(unsigned long PID) { + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + if (ExecutingSeedCorpora) + Printf("\nINFO: a leak has been found in the initial corpus.\n\n"); + Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n"); + DumpCurrentUnit("leak-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. +} + +unsigned long Fuzzer::GetPid(unsigned long PID) { return fuzzer::GetPid(); } + +void Fuzzer::PrintStackTrace(unsigned long PID) { fuzzer::PrintStackTrace(); } + +void Fuzzer::PrintMemoryProfile(unsigned long PID) { + fuzzer::PrintMemoryProfile(); +} + void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units, size_t Features) { size_t ExecPerSec = execPerSec(); @@ -675,6 +675,7 @@ return; // No lsan. // Run the target once again, but with lsan disabled so that if there is // a real leak we do not report it twice. + ExecutingSeedCorpora = DuringInitialCorpusExecution; EF->__lsan_disable(); ExecuteCallback(Data, Size); EF->__lsan_enable(); @@ -694,13 +695,8 @@ // Now perform the actual lsan pass. This is expensive and we must ensure // we don't call it too often. if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it. - if (DuringInitialCorpusExecution) - Printf("\nINFO: a leak has been found in the initial corpus.\n\n"); - Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n"); CurrentUnitSize = Size; - DumpCurrentUnit("leak-"); - PrintFinalStats(); - _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. + LeakCallback(0); } } @@ -920,4 +916,64 @@ return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); } +ATTRIBUTE_INTERFACE +void FuzzerAlarmCallback() { + assert(fuzzer::F); + fuzzer::F->AlarmCallback(); +} + +ATTRIBUTE_INTERFACE +void FuzzerDeathCallback() { + assert(fuzzer::F); + fuzzer::F->DeathCallback(); +} + +ATTRIBUTE_INTERFACE +void FuzzerCrashSignalCallback(unsigned long PID) { + assert(fuzzer::F); + fuzzer::F->CrashCallback(PID); +} + +ATTRIBUTE_INTERFACE +void FuzzerExitCallback(unsigned long PID) { + assert(fuzzer::F); + fuzzer::F->ExitCallback(PID); +} + +ATTRIBUTE_INTERFACE +void FuzzerFileSizeExceedCallback() { + assert(fuzzer::F); + fuzzer::F->FileSizeExceedCallback(); +} + +ATTRIBUTE_INTERFACE +void FuzzerGracefulExitCallback() { + assert(fuzzer::F); + fuzzer::F->GracefulExitCallback(); +} + +ATTRIBUTE_INTERFACE +void FuzzerInterruptCallback() { + assert(fuzzer::F); + fuzzer::F->InterruptCallback(); +} + +ATTRIBUTE_INTERFACE +void FuzzerLeakCallback(unsigned long PID) { + assert(fuzzer::F); + fuzzer::F->LeakCallback(PID); +} + +ATTRIBUTE_INTERFACE +void FuzzerMallocLimitCallback(unsigned long PID, size_t Size) { + assert(fuzzer::F); + fuzzer::F->OutOfMemoryCallback(PID, Size); +} + +ATTRIBUTE_INTERFACE +void FuzzerRssLimitCallback(unsigned long PID) { + assert(fuzzer::F); + fuzzer::F->OutOfMemoryCallback(PID, 0); +} + } // extern "C" Index: compiler-rt/lib/fuzzer/FuzzerMonitor.h =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/FuzzerMonitor.h @@ -0,0 +1,30 @@ +//===- FuzzerMonitor.h ------------------------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Interfaces for trace mallocs/frees and calling back on errors. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MONITOR_H +#define LLVM_FUZZER_MONITOR_H + +extern "C" { + +ATTRIBUTE_INTERFACE void FuzzerAlarmCallback(); +ATTRIBUTE_INTERFACE void FuzzerCrashSignalCallback(unsigned long PID); +ATTRIBUTE_INTERFACE void FuzzerDeathCallback(); +ATTRIBUTE_INTERFACE void FuzzerExitCallback(unsigned long PID); +ATTRIBUTE_INTERFACE void FuzzerFileSizeExceedCallback(); +ATTRIBUTE_INTERFACE void FuzzerGracefulExitCallback(); +ATTRIBUTE_INTERFACE void FuzzerInterruptCallback(); +ATTRIBUTE_INTERFACE void FuzzerLeakCallback(unsigned long PID); +ATTRIBUTE_INTERFACE void FuzzerMallocLimitCallback(unsigned long PID, + size_t Size); +ATTRIBUTE_INTERFACE void FuzzerRssLimitCallback(unsigned long PID); + +} // extern "C" + +#endif // LLVM_FUZZER_MONITOR_H Index: compiler-rt/lib/fuzzer/FuzzerUtil.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerUtil.h +++ compiler-rt/lib/fuzzer/FuzzerUtil.h @@ -42,7 +42,6 @@ std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC); void PrintStackTrace(); - void PrintMemoryProfile(); unsigned NumberOfCpuCores(); Index: compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp +++ compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp @@ -64,7 +64,7 @@ void AlarmHandler(int Seconds) { while (true) { SleepSeconds(Seconds); - Fuzzer::StaticAlarmCallback(); + FuzzerAlarmCallback(); } } @@ -85,7 +85,7 @@ constexpr size_t CFAOffset = (sizeof(zx_thread_state_general_regs_t) + 15) & -(uintptr_t)16; #endif -// For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback +// For the crash handler, we need to call FuzzerCrashSignalCallback // without POSIX signal handlers. To achieve this, we use an assembly function // to add the necessary CFI unwinding information and a C function to bridge // from that back into C++. @@ -172,7 +172,7 @@ // callback. __attribute__((noreturn)) static void StaticCrashHandler() { - Fuzzer::StaticCrashSignalCallback(); + FuzzerCrashSignalCallback(); for (;;) { _Exit(1); } Index: compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp +++ compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp @@ -11,6 +11,7 @@ #if LIBFUZZER_POSIX #include "FuzzerIO.h" #include "FuzzerInternal.h" +#include "FuzzerMonitor.h" #include "FuzzerTracePC.h" #include #include @@ -29,9 +30,7 @@ namespace fuzzer { -static void AlarmHandler(int, siginfo_t *, void *) { - Fuzzer::StaticAlarmCallback(); -} +static void AlarmHandler(int, siginfo_t *, void *) { FuzzerAlarmCallback(); } static void (*upstream_segv_handler)(int, siginfo_t *, void *); @@ -39,23 +38,23 @@ assert(si->si_signo == SIGSEGV); if (upstream_segv_handler) return upstream_segv_handler(sig, si, ucontext); - Fuzzer::StaticCrashSignalCallback(); + FuzzerCrashSignalCallback(0); } static void CrashHandler(int, siginfo_t *, void *) { - Fuzzer::StaticCrashSignalCallback(); + FuzzerCrashSignalCallback(0); } static void InterruptHandler(int, siginfo_t *, void *) { - Fuzzer::StaticInterruptCallback(); + FuzzerInterruptCallback(); } static void GracefulExitHandler(int, siginfo_t *, void *) { - Fuzzer::StaticGracefulExitCallback(); + FuzzerGracefulExitCallback(); } static void FileSizeExceedHandler(int, siginfo_t *, void *) { - Fuzzer::StaticFileSizeExceedCallback(); + FuzzerFileSizeExceedCallback(); } static void SetSigaction(int signum, Index: compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp +++ compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp @@ -36,17 +36,17 @@ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: case EXCEPTION_STACK_OVERFLOW: if (HandlerOpt->HandleSegv) - Fuzzer::StaticCrashSignalCallback(); + FuzzerCrashSignalCallback(); break; case EXCEPTION_DATATYPE_MISALIGNMENT: case EXCEPTION_IN_PAGE_ERROR: if (HandlerOpt->HandleBus) - Fuzzer::StaticCrashSignalCallback(); + FuzzerCrashSignalCallback(); break; case EXCEPTION_ILLEGAL_INSTRUCTION: case EXCEPTION_PRIV_INSTRUCTION: if (HandlerOpt->HandleIll) - Fuzzer::StaticCrashSignalCallback(); + FuzzerCrashSignalCallback(); break; case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_DIVIDE_BY_ZERO: @@ -58,7 +58,7 @@ case EXCEPTION_INT_DIVIDE_BY_ZERO: case EXCEPTION_INT_OVERFLOW: if (HandlerOpt->HandleFpe) - Fuzzer::StaticCrashSignalCallback(); + FuzzerCrashSignalCallback(); break; // This is an undocumented exception code corresponding to a Visual C++ // Exception. @@ -77,19 +77,17 @@ switch (dwCtrlType) { case CTRL_C_EVENT: if (HandlerOpt->HandleInt) - Fuzzer::StaticInterruptCallback(); + FuzzerInterruptCallback(); return TRUE; case CTRL_BREAK_EVENT: if (HandlerOpt->HandleTerm) - Fuzzer::StaticInterruptCallback(); + FuzzerInterruptCallback(); return TRUE; } return FALSE; } -void CALLBACK AlarmHandler(PVOID, BOOLEAN) { - Fuzzer::StaticAlarmCallback(); -} +void CALLBACK AlarmHandler(PVOID, BOOLEAN) { FuzzerAlarmCallback(); } class TimerQ { HANDLE TimerQueue; @@ -118,7 +116,7 @@ static TimerQ Timer; -static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); } +static void CrashHandler(int) { FuzzerCrashSignalCallback(); } void SetSignalHandler(const FuzzingOptions& Options) { HandlerOpt = &Options;