Index: include/llvm/FuzzMutate/FuzzerCLI.h =================================================================== --- include/llvm/FuzzMutate/FuzzerCLI.h +++ include/llvm/FuzzMutate/FuzzerCLI.h @@ -68,6 +68,12 @@ /// 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 Index: lib/FuzzMutate/FuzzerCLI.cpp =================================================================== --- lib/FuzzMutate/FuzzerCLI.cpp +++ 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 @@ 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; +} Index: tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp =================================================================== --- tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp +++ tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp @@ -84,8 +84,8 @@ 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; } Index: tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp =================================================================== --- tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp +++ tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp @@ -57,23 +57,45 @@ "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 @@ // 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; }