diff --git a/llvm/lib/Fuzzer/FuzzerInternal.h b/llvm/lib/Fuzzer/FuzzerInternal.h index 7d8247327f6b..d9331c662de6 100644 --- a/llvm/lib/Fuzzer/FuzzerInternal.h +++ b/llvm/lib/Fuzzer/FuzzerInternal.h @@ -415,8 +415,8 @@ private: void PrintStats(const char *Where, const char *End = "\n"); void PrintStatusForNewUnit(const Unit &U); void ShuffleCorpus(UnitVector *V); - void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size); - void CheckForMemoryLeaks(); + void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution); // Updates the probability distribution for the units in the corpus. // Must be called whenever the corpus or unit weights are changed. diff --git a/llvm/lib/Fuzzer/FuzzerLoop.cpp b/llvm/lib/Fuzzer/FuzzerLoop.cpp index f91f1281ee01..d924e5d8fb00 100644 --- a/llvm/lib/Fuzzer/FuzzerLoop.cpp +++ b/llvm/lib/Fuzzer/FuzzerLoop.cpp @@ -409,13 +409,14 @@ void Fuzzer::ShuffleAndMinimize() { if (Options.Verbosity >= 2) Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size()); } + TryDetectingAMemoryLeak(U.data(), U.size(), + /*DuringInitialCorpusExecution*/ true); } Corpus = NewCorpus; UpdateCorpusDistribution(); for (auto &X : Corpus) UnitHashesAddedToCorpus.insert(Hash(X)); PrintStats("INITED"); - CheckForMemoryLeaks(); } bool Fuzzer::UpdateMaxCoverage() { @@ -639,26 +640,10 @@ void Fuzzer::Merge(const std::vector &Corpora) { Printf("=== Merge: written %zd units\n", Res.size()); } -// Tries to call lsan, and if there are leaks exits. We call this right after -// the initial corpus was read because if there are leaky inputs in the corpus -// further fuzzing will likely hit OOMs. -void Fuzzer::CheckForMemoryLeaks() { - if (!Options.DetectLeaks) return; - if (!__lsan_do_recoverable_leak_check) - return; - if (__lsan_do_recoverable_leak_check()) { - Printf("==%d== ERROR: libFuzzer: initial corpus triggers memory leaks.\n" - "Exiting now. Use -detect_leaks=0 to disable leak detection here.\n" - "LeakSanitizer will still check for leaks at the process exit.\n", - GetPid()); - PrintFinalStats(); - _Exit(Options.ErrorExitCode); - } -} - // Tries detecting a memory leak on the particular input that we have just // executed before calling this function. -void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size) { +void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution) { if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely. if (!Options.DetectLeaks) return; if (!&__lsan_enable || !&__lsan_disable || !__lsan_do_recoverable_leak_check) @@ -681,6 +666,9 @@ void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size) { // Now perform the actual lsan pass. This is expensive and we must ensure // we don't call it too often. if (__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"); CurrentUnitData = Data; CurrentUnitSize = Size; DumpCurrentUnit("leak-"); @@ -715,7 +703,8 @@ void Fuzzer::MutateAndTestOne() { StartTraceRecording(); RunOneAndUpdateCorpus(MutateInPlaceHere.data(), Size); StopTraceRecording(); - TryDetectingAMemoryLeak(MutateInPlaceHere.data(), Size); + TryDetectingAMemoryLeak(MutateInPlaceHere.data(), Size, + /*DuringInitialCorpusExecution*/ false); } } diff --git a/llvm/lib/Fuzzer/test/fuzzer-leak.test b/llvm/lib/Fuzzer/test/fuzzer-leak.test index 48e9ca62ac96..184e199ff14b 100644 --- a/llvm/lib/Fuzzer/test/fuzzer-leak.test +++ b/llvm/lib/Fuzzer/test/fuzzer-leak.test @@ -7,7 +7,7 @@ LEAK_DURING-NOT: DEATH: RUN: not LLVMFuzzer-LeakTest -runs=0 -detect_leaks=1 %S 2>&1 | FileCheck %s --check-prefix=LEAK_IN_CORPUS LEAK_IN_CORPUS: ERROR: LeakSanitizer: detected memory leaks -LEAK_IN_CORPUS: ERROR: libFuzzer: initial corpus triggers memory leaks. +LEAK_IN_CORPUS: INFO: a leak has been found in the initial corpus. RUN: not LLVMFuzzer-LeakTest -runs=100000 -detect_leaks=0 2>&1 | FileCheck %s --check-prefix=LEAK_AFTER