From 4f2f540e7595c6b02fc3e11245388f632af165e1 Mon Sep 17 00:00:00 2001 From: Igor Laevsky Date: Mon, 5 Feb 2018 11:05:47 +0000 Subject: [PATCH] [llvm-opt-fuzzer] Avoid adding incorrect inputs to the fuzzer corpus Differential Revision: https://reviews.llvm.org/D42414 llvm-svn: 324225 --- include/llvm/FuzzMutate/FuzzerCLI.h | 6 ++++ lib/FuzzMutate/FuzzerCLI.cpp | 10 ++++++ tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp | 4 +-- tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp | 38 ++++++++++++++++----- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/include/llvm/FuzzMutate/FuzzerCLI.h b/include/llvm/FuzzMutate/FuzzerCLI.h index a775fdfb603..3333e96db16 100644 --- a/include/llvm/FuzzMutate/FuzzerCLI.h +++ b/include/llvm/FuzzMutate/FuzzerCLI.h @@ -68,6 +68,12 @@ std::unique_ptr parseModule(const uint8_t *Data, size_t Size, /// returns 0 and leaves Dest unchanged. size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize); +/// Try to parse module and verify it. May output verification errors to the +/// errs(). +/// \return New module or nullptr in case of error. +std::unique_ptr parseAndVerify(const uint8_t *Data, size_t Size, + LLVMContext &Context); + } // end llvm namespace #endif // LLVM_FUZZMUTATE_FUZZER_CLI_H diff --git a/lib/FuzzMutate/FuzzerCLI.cpp b/lib/FuzzMutate/FuzzerCLI.cpp index 2d8973d6903..43698c605a2 100644 --- a/lib/FuzzMutate/FuzzerCLI.cpp +++ b/lib/FuzzMutate/FuzzerCLI.cpp @@ -18,6 +18,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/IR/Verifier.h" using namespace llvm; @@ -175,3 +176,12 @@ size_t llvm::writeModule(const Module &M, uint8_t *Dest, size_t MaxSize) { memcpy(Dest, Buf.data(), Buf.size()); return Buf.size(); } + +std::unique_ptr llvm::parseAndVerify(const uint8_t *Data, size_t Size, + LLVMContext &Context) { + auto M = parseModule(Data, Size, Context); + if (!M || verifyModule(*M, &errs())) + return nullptr; + + return M; +} diff --git a/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp b/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp index 764134d18b7..73d6015ff53 100644 --- a/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp +++ b/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp @@ -84,8 +84,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; LLVMContext Context; - auto M = parseModule(Data, Size, Context); - if (!M || verifyModule(*M, &errs())) { + auto M = parseAndVerify(Data, Size, Context); + if (!M) { errs() << "error: input module is broken!\n"; return 0; } diff --git a/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp b/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp index 8187bbcea66..39909ecd4e0 100644 --- a/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp +++ b/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp @@ -57,23 +57,45 @@ extern "C" LLVM_ATTRIBUTE_USED size_t LLVMFuzzerCustomMutator( "IR mutator should have been created during fuzzer initialization"); LLVMContext Context; - auto M = parseModule(Data, Size, Context); - if (!M || verifyModule(*M, &errs())) { + auto M = parseAndVerify(Data, Size, Context); + if (!M) { errs() << "error: mutator input module is broken!\n"; return 0; } Mutator->mutateModule(*M, Seed, Size, MaxSize); -#ifndef NDEBUG if (verifyModule(*M, &errs())) { errs() << "mutation result doesn't pass verification\n"; M->dump(); - abort(); + // Avoid adding incorrect test cases to the corpus. + return 0; + } + + std::string Buf; + { + raw_string_ostream OS(Buf); + WriteBitcodeToFile(M.get(), OS); + } + if (Buf.size() > MaxSize) + return 0; + + // There are some invariants which are not checked by the verifier in favor + // of having them checked by the parser. They may be considered as bugs in the + // verifier and should be fixed there. However until all of those are covered + // we want to check for them explicitly. Otherwise we will add incorrect input + // to the corpus and this is going to confuse the fuzzer which will start + // exploration of the bitcode reader error handling code. + auto NewM = parseAndVerify( + reinterpret_cast(Buf.data()), Buf.size(), Context); + if (!NewM) { + errs() << "mutator failed to re-read the module\n"; + M->dump(); + return 0; } -#endif - return writeModule(*M, Data, MaxSize); + memcpy(Data, Buf.data(), Buf.size()); + return Buf.size(); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { @@ -87,8 +109,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { // LLVMContext Context; - auto M = parseModule(Data, Size, Context); - if (!M || verifyModule(*M, &errs())) { + auto M = parseAndVerify(Data, Size, Context); + if (!M) { errs() << "error: input module is broken!\n"; return 0; }