//===--- llvm-opt-fuzzer.cpp - Fuzzer for instruction selection ----------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Tool to fuzz optimization passes using libFuzzer. // //===----------------------------------------------------------------------===// #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/CodeGen/CommandFlags.inc" #include "llvm/FuzzMutate/FuzzerCLI.h" #include "llvm/FuzzMutate/IRMutator.h" #include "llvm/IR/Verifier.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" using namespace llvm; static cl::opt TargetTripleStr("mtriple", cl::desc("Override target triple for module")); // Passes to run for this fuzzer instance. Expects new pass manager syntax. static cl::opt PassPipeline( "passes", cl::desc("A textual description of the pass pipeline for testing")); static std::unique_ptr Mutator; static std::unique_ptr TM; std::unique_ptr createOptMutator() { std::vector Types{ Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty, Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; std::vector> Strategies; Strategies.push_back( llvm::make_unique( InjectorIRStrategy::getDefaultOps())); Strategies.push_back( llvm::make_unique()); return llvm::make_unique(std::move(Types), std::move(Strategies)); } extern "C" LLVM_ATTRIBUTE_USED size_t LLVMFuzzerCustomMutator( uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed) { assert(Mutator && "IR mutator should have been created during fuzzer initialization"); LLVMContext Context; auto M = parseAndVerify(Data, Size, Context); if (!M) { errs() << "error: mutator input module is broken!\n"; return 0; } Mutator->mutateModule(*M, Seed, Size, MaxSize); if (verifyModule(*M, &errs())) { errs() << "mutation result doesn't pass verification\n"; #ifndef NDEBUG M->dump(); #endif // Avoid adding incorrect test cases to the corpus. return 0; } std::string Buf; { raw_string_ostream OS(Buf); WriteBitcodeToFile(*M, 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"; #ifndef NDEBUG M->dump(); #endif return 0; } memcpy(Data, Buf.data(), Buf.size()); return Buf.size(); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { assert(TM && "Should have been created during fuzzer initialization"); if (Size <= 1) // We get bogus data given an empty corpus - ignore it. return 0; // Parse module // LLVMContext Context; auto M = parseAndVerify(Data, Size, Context); if (!M) { errs() << "error: input module is broken!\n"; return 0; } // Set up target dependant options // M->setTargetTriple(TM->getTargetTriple().normalize()); M->setDataLayout(TM->createDataLayout()); setFunctionAttributes(TM->getTargetCPU(), TM->getTargetFeatureString(), *M); // Create pass pipeline // PassBuilder PB(TM.get()); LoopAnalysisManager LAM; FunctionAnalysisManager FAM; CGSCCAnalysisManager CGAM; ModulePassManager MPM; ModuleAnalysisManager MAM; FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); }); PB.registerModuleAnalyses(MAM); PB.registerCGSCCAnalyses(CGAM); PB.registerFunctionAnalyses(FAM); PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); bool Ok = PB.parsePassPipeline(MPM, PassPipeline, false, false); assert(Ok && "Should have been checked during fuzzer initialization"); (void)Ok; // silence unused variable warning on release builds // Run passes which we need to test // MPM.run(*M, MAM); // Check that passes resulted in a correct code if (verifyModule(*M, &errs())) { errs() << "Transformation resulted in an invalid module\n"; abort(); } return 0; } static void handleLLVMFatalError(void *, const std::string &Message, bool) { // TODO: Would it be better to call into the fuzzer internals directly? dbgs() << "LLVM ERROR: " << Message << "\n" << "Aborting to trigger fuzzer exit handling.\n"; abort(); } extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize( int *argc, char ***argv) { EnableDebugBuffering = true; // Make sure we print the summary and the current unit when LLVM errors out. install_fatal_error_handler(handleLLVMFatalError, nullptr); // Initialize llvm // InitializeAllTargets(); InitializeAllTargetMCs(); PassRegistry &Registry = *PassRegistry::getPassRegistry(); initializeCore(Registry); initializeCoroutines(Registry); initializeScalarOpts(Registry); initializeObjCARCOpts(Registry); initializeVectorization(Registry); initializeIPO(Registry); initializeAnalysis(Registry); initializeTransformUtils(Registry); initializeInstCombine(Registry); initializeAggressiveInstCombine(Registry); initializeInstrumentation(Registry); initializeTarget(Registry); // Parse input options // handleExecNameEncodedOptimizerOpts(*argv[0]); parseFuzzerCLOpts(*argc, *argv); // Create TargetMachine // if (TargetTripleStr.empty()) { errs() << *argv[0] << ": -mtriple must be specified\n"; exit(1); } Triple TargetTriple = Triple(Triple::normalize(TargetTripleStr)); std::string Error; const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TargetTriple, Error); if (!TheTarget) { errs() << *argv[0] << ": " << Error; exit(1); } TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); TM.reset(TheTarget->createTargetMachine( TargetTriple.getTriple(), getCPUStr(), getFeaturesStr(), Options, getRelocModel(), getCodeModel(), CodeGenOpt::Default)); assert(TM && "Could not allocate target machine!"); // Check that pass pipeline is specified and correct // if (PassPipeline.empty()) { errs() << *argv[0] << ": at least one pass should be specified\n"; exit(1); } PassBuilder PB(TM.get()); ModulePassManager MPM; if (!PB.parsePassPipeline(MPM, PassPipeline, false, false)) { errs() << *argv[0] << ": can't parse pass pipeline\n"; exit(1); } // Create mutator // Mutator = createOptMutator(); return 0; }