[compiler-rt] 92fb310 - [libFuzzer] Extend the fuzz target intarface to allow -1 return value.
Kostya Serebryany via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 30 13:21:43 PDT 2022
Author: Kostya Serebryany
Date: 2022-06-30T13:21:27-07:00
New Revision: 92fb310151d2b1e349695fc0f1c5d5d50afb3b52
URL: https://github.com/llvm/llvm-project/commit/92fb310151d2b1e349695fc0f1c5d5d50afb3b52
DIFF: https://github.com/llvm/llvm-project/commit/92fb310151d2b1e349695fc0f1c5d5d50afb3b52.diff
LOG: [libFuzzer] Extend the fuzz target intarface to allow -1 return value.
With this change, fuzz targets may choose to return -1
to indicate that the input should not be added to the corpus
regardless of the coverage it generated.
Reviewed By: morehouse
Differential Revision: https://reviews.llvm.org/D128749
Added:
compiler-rt/test/fuzzer/Reject.cpp
compiler-rt/test/fuzzer/reject.test
Modified:
compiler-rt/lib/fuzzer/FuzzerInternal.h
compiler-rt/lib/fuzzer/FuzzerLoop.cpp
compiler-rt/test/fuzzer/not-instrumented.test
llvm/docs/LibFuzzer.rst
Removed:
################################################################################
diff --git a/compiler-rt/lib/fuzzer/FuzzerInternal.h b/compiler-rt/lib/fuzzer/FuzzerInternal.h
index 6637b0034e552..31f54eaa478a8 100644
--- a/compiler-rt/lib/fuzzer/FuzzerInternal.h
+++ b/compiler-rt/lib/fuzzer/FuzzerInternal.h
@@ -65,7 +65,10 @@ class Fuzzer {
static void StaticFileSizeExceedCallback();
static void StaticGracefulExitCallback();
- void ExecuteCallback(const uint8_t *Data, size_t Size);
+ // Executes the target callback on {Data, Size} once.
+ // Returns false if the input was rejected by the target (target returned -1),
+ // and true otherwise.
+ bool ExecuteCallback(const uint8_t *Data, size_t Size);
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
InputInfo *II = nullptr, bool ForceAddToCorpus = false,
bool *FoundUniqFeatures = nullptr);
diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
index 3205942f6d84d..f095757229e9e 100644
--- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
@@ -511,7 +511,7 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
// Largest input length should be INT_MAX.
assert(Size < std::numeric_limits<uint32_t>::max());
- ExecuteCallback(Data, Size);
+ if(!ExecuteCallback(Data, Size)) return false;
auto TimeOfUnit = duration_cast<microseconds>(UnitStopTime - UnitStartTime);
UniqFeatureSetTmp.clear();
@@ -586,7 +586,7 @@ static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) {
// This method is not inlined because it would cause a test to fail where it
// is part of the stack unwinding. See D97975 for details.
-ATTRIBUTE_NOINLINE void Fuzzer::ExecuteCallback(const uint8_t *Data,
+ATTRIBUTE_NOINLINE bool Fuzzer::ExecuteCallback(const uint8_t *Data,
size_t Size) {
TPC.RecordInitialStack();
TotalNumberOfRuns++;
@@ -602,23 +602,24 @@ ATTRIBUTE_NOINLINE void Fuzzer::ExecuteCallback(const uint8_t *Data,
if (CurrentUnitData && CurrentUnitData != Data)
memcpy(CurrentUnitData, Data, Size);
CurrentUnitSize = Size;
+ int CBRes = 0;
{
ScopedEnableMsanInterceptorChecks S;
AllocTracer.Start(Options.TraceMalloc);
UnitStartTime = system_clock::now();
TPC.ResetMaps();
RunningUserCallback = true;
- int Res = CB(DataCopy, Size);
+ CBRes = CB(DataCopy, Size);
RunningUserCallback = false;
UnitStopTime = system_clock::now();
- (void)Res;
- assert(Res == 0);
+ assert(CBRes == 0 || CBRes == -1);
HasMoreMallocsThanFrees = AllocTracer.Stop();
}
if (!LooseMemeq(DataCopy, Data, Size))
CrashOnOverwrittenData();
CurrentUnitSize = 0;
delete[] DataCopy;
+ return CBRes == 0;
}
std::string Fuzzer::WriteToOutputCorpus(const Unit &U) {
@@ -843,9 +844,16 @@ void Fuzzer::ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles) {
}
if (Corpus.empty() && Options.MaxNumberOfRuns) {
- Printf("ERROR: no interesting inputs were found. "
- "Is the code instrumented for coverage? Exiting.\n");
- exit(1);
+ Printf("WARNING: no interesting inputs were found so far. "
+ "Is the code instrumented for coverage?\n"
+ "This may also happen if the target rejected all inputs we tried so "
+ "far\n");
+ // The remaining logic requires that the corpus is not empty,
+ // so we add one fake input to the in-memory corpus.
+ Corpus.AddToCorpus({'\n'}, /*NumFeatures=*/1, /*MayDeleteFile=*/true,
+ /*HasFocusFunction=*/false, /*NeverReduce=*/false,
+ /*TimeOfUnit=*/duration_cast<microseconds>(0s), {0}, DFT,
+ /*BaseII*/ nullptr);
}
}
diff --git a/compiler-rt/test/fuzzer/Reject.cpp b/compiler-rt/test/fuzzer/Reject.cpp
new file mode 100644
index 0000000000000..d67085cd50102
--- /dev/null
+++ b/compiler-rt/test/fuzzer/Reject.cpp
@@ -0,0 +1,23 @@
+// 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
+
+// Tests how the fuzzer rejects inputs if the target returns -1.
+#include <cstddef>
+#include <cstdint>
+
+static volatile int Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size != 3)
+ return -1; // Reject anyting that's not 3 bytes long.
+ // Reject 'rej'.
+ if (Data[0] == 'r' && Data[1] == 'e' && Data[2] == 'j')
+ return -1;
+ // Accept 'acc'.
+ if (Data[0] == 'a' && Data[1] == 'c' && Data[2] == 'c') {
+ Sink = 1;
+ return 0;
+ }
+ return 0;
+}
diff --git a/compiler-rt/test/fuzzer/not-instrumented.test b/compiler-rt/test/fuzzer/not-instrumented.test
index 2330c47700675..6bf51f05c0f26 100644
--- a/compiler-rt/test/fuzzer/not-instrumented.test
+++ b/compiler-rt/test/fuzzer/not-instrumented.test
@@ -1,4 +1,4 @@
RUN: %cpp_compiler %S/NotinstrumentedTest.cpp -fsanitize-coverage=0 -o %t-NotinstrumentedTest-NoCoverage
-RUN: not %run %t-NotinstrumentedTest-NoCoverage 2>&1 | FileCheck %s --check-prefix=NO_COVERAGE
+RUN: %run %t-NotinstrumentedTest-NoCoverage -runs=100 2>&1 | FileCheck %s --check-prefix=NO_COVERAGE
-NO_COVERAGE: ERROR: no interesting inputs were found. Is the code instrumented for coverage? Exiting
+NO_COVERAGE: WARNING: no interesting inputs were found so far. Is the code instrumented for coverage?
diff --git a/compiler-rt/test/fuzzer/reject.test b/compiler-rt/test/fuzzer/reject.test
new file mode 100644
index 0000000000000..b7b7391ba89e1
--- /dev/null
+++ b/compiler-rt/test/fuzzer/reject.test
@@ -0,0 +1,9 @@
+# Runs the Reject.cpp test,
+# ensures that the input 'acc' is present in the corpus,
+# and the input 'rej' is not.
+
+RUN: rm -rf %t-corpus && mkdir %t-corpus
+RUN: %cpp_compiler %S/Reject.cpp -o %t-Reject
+RUN: %run %t-Reject -runs=1000000 %t-corpus
+RUN: grep 'acc' %t-corpus/*
+RUN: not grep 'rej' %t-corpus/*
diff --git a/llvm/docs/LibFuzzer.rst b/llvm/docs/LibFuzzer.rst
index d4374602e7c1a..89265ebfe35ec 100644
--- a/llvm/docs/LibFuzzer.rst
+++ b/llvm/docs/LibFuzzer.rst
@@ -49,7 +49,7 @@ Like this:
// fuzz_target.cc
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
DoSomethingInterestingWithMyAPI(Data, Size);
- return 0; // Non-zero return values are reserved for future use.
+ return 0; // Values other than 0 and -1 are reserved for future use.
}
Note that this fuzz target does not depend on libFuzzer in any way
@@ -646,6 +646,28 @@ arguments and a callback. This callback is invoked just like
int (*UserCb)(const uint8_t *Data, size_t Size));
+Rejecting unwanted inputs
+-------------------------
+
+It may be desirable to reject some inputs, i.e. to not add them to the corpus.
+
+For example, when fuzzing an API consisting of parsing and other logic,
+one may want to allow only those inputs into the corpus that parse successfully.
+
+If the fuzz target returns -1 on a given input,
+libFuzzer will not add that input top the corpus, regardless of what coverage
+it triggers.
+
+
+.. code-block:: c++
+
+ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (auto *Obj = ParseMe(Data, Size)) {
+ Obj->DoSomethingInteresting();
+ return 0; // Accept. The input may be added to the corpus.
+ }
+ return -1; // Reject; The input will not be added to the corpus.
+ }
Leaks
-----
More information about the llvm-commits
mailing list