Index: clang/tools/clang-fuzzer/CMakeLists.txt =================================================================== --- clang/tools/clang-fuzzer/CMakeLists.txt +++ clang/tools/clang-fuzzer/CMakeLists.txt @@ -79,12 +79,12 @@ ${ProtobufMutator_LIBRARIES} ${PROTOBUF_LIBRARIES} ${LLVM_LIB_FUZZING_ENGINE} - clangFuzzerInitialize ) target_link_libraries(clang-proto-fuzzer PRIVATE ${COMMON_PROTO_FUZZ_LIBRARIES} + clangFuzzerInitialize clangHandleCXX clangCXXProto clangProtoToCXX @@ -92,6 +92,7 @@ target_link_libraries(clang-loop-proto-fuzzer PRIVATE ${COMMON_PROTO_FUZZ_LIBRARIES} + clangFuzzerInitialize clangHandleCXX clangCXXLoopProto clangLoopProtoToCXX Index: clang/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp =================================================================== --- clang/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp +++ clang/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp @@ -15,7 +15,7 @@ //===----------------------------------------------------------------------===// #include "cxx_loop_proto.pb.h" -#include "fuzzer-initialize/fuzzer_initialize.h" +#include "handle-llvm/fuzzer_initialize.h" #include "handle-llvm/handle_llvm.h" #include "proto-to-llvm/loop_proto_to_llvm.h" #include "src/libfuzzer/libfuzzer_macro.h" @@ -24,5 +24,5 @@ DEFINE_BINARY_PROTO_FUZZER(const LoopFunction &input) { auto S = LoopFunctionToLLVMString(input); - HandleLLVM(S, GetCLArgs()); + HandleLLVM(S, GetCLArgs(), GetRegion1(), GetRegion2()); } Index: clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt =================================================================== --- clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt +++ clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt @@ -1,5 +1,29 @@ -set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Support) +set(LLVM_LINK_COMPONENTS + CodeGen + Core + ExecutionEngine + IRReader + MC + MCJIT + Object + RuntimeDyld + SelectionDAG + Support + Target + TransformUtils + native +) + +# Depend on LLVM IR instrinsic generation. +set(handle_llvm_deps intrinsics_gen) +if (CLANG_BUILT_STANDALONE) + set(handle_llvm_deps) +endif() add_clang_library(clangHandleLLVM handle_llvm.cpp + fuzzer_initialize.cpp + + DEPENDS + ${handle_llvm_deps} ) Index: clang/tools/clang-fuzzer/handle-llvm/fuzzer_initialize.h =================================================================== --- clang/tools/clang-fuzzer/handle-llvm/fuzzer_initialize.h +++ clang/tools/clang-fuzzer/handle-llvm/fuzzer_initialize.h @@ -1,4 +1,4 @@ -//==-- handle_llvm.h - Helper function for Clang fuzzers -------------------==// +//==-- fuzzer_initialize.h - Fuzz Clang ------------------------------------==// // // The LLVM Compiler Infrastructure // @@ -7,19 +7,15 @@ // //===----------------------------------------------------------------------===// // -// Defines HandleLLVM for use by the Clang fuzzers. +// Defines a function that returns the command line arguments for a specific +// call to the fuzz target. // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_LLVM_HANDLELLVM_H -#define LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_LLVM_HANDLELLVM_H - -#include #include namespace clang_fuzzer { -void HandleLLVM(const std::string &S, - const std::vector &ExtraArgs); -} // namespace clang_fuzzer - -#endif +const std::vector& GetCLArgs(); +char *GetRegion1(); +char *GetRegion2(); +} Index: clang/tools/clang-fuzzer/handle-llvm/fuzzer_initialize.cpp =================================================================== --- /dev/null +++ clang/tools/clang-fuzzer/handle-llvm/fuzzer_initialize.cpp @@ -0,0 +1,69 @@ +//===-- fuzzer_initialize.cpp - Fuzz Clang --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a few functions: one that returns the command line +/// arguments for a given call to the fuzz target and one that initializes +/// the fuzzer with the correct command line arguments. It also initializes two +/// regions in memory for the fuzzer to use to insert and execute generated +/// code. Getter methods for those two regions are also included. +/// +//===----------------------------------------------------------------------===// + +#include "fuzzer_initialize.h" + +#include "llvm/Support/TargetSelect.h" +#include +#include + +using namespace clang_fuzzer; + + +namespace clang_fuzzer { + +static std::vector CLArgs; +static char *m1; +static char *m2; + +const std::vector& GetCLArgs() { + return CLArgs; +} + +char *GetRegion1() { + return m1; +} + +char *GetRegion2() { + return m2; +} + +} + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllAsmParsers(); + + CLArgs.push_back("-O2"); + for (int I = 1; I < *argc; I++) { + if (strcmp((*argv)[I], "-ignore_remaining_args=1") == 0) { + for (I++; I < *argc; I++) + CLArgs.push_back((*argv)[I]); + break; + } + } + + m1 = (char *) mmap(NULL, 1000000, PROT_WRITE | PROT_EXEC, + MAP_SHARED | MAP_ANON, -1, 0); + m2 = (char *) mmap(NULL, 1000000, PROT_WRITE | PROT_EXEC, + MAP_SHARED | MAP_ANON, -1, 0); + + return 0; +} Index: clang/tools/clang-fuzzer/handle-llvm/handle_llvm.h =================================================================== --- clang/tools/clang-fuzzer/handle-llvm/handle_llvm.h +++ clang/tools/clang-fuzzer/handle-llvm/handle_llvm.h @@ -19,7 +19,8 @@ namespace clang_fuzzer { void HandleLLVM(const std::string &S, - const std::vector &ExtraArgs); + const std::vector &ExtraArgs, + char *m1, char *m2); } // namespace clang_fuzzer #endif Index: clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp =================================================================== --- clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp +++ clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp @@ -7,8 +7,10 @@ // //===----------------------------------------------------------------------===// // -// Implements HandleLLVM for use by the Clang fuzzers. Mimics the llc tool to -// compile an LLVM IR file to X86_64 assembly. +// Implements HandleLLVM for use by the Clang fuzzers. First runs an loop +// vectorizer optimization pass over the given IR code. Then mimics lli on both +// versions to JIT the generated code and execute it. Currently, functions are +// executed on dummy inputs. // //===----------------------------------------------------------------------===// @@ -16,24 +18,42 @@ #include "llvm/ADT/Triple.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/CommandFlags.inc" #include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/ExecutionEngine/ObjectCache.h" +#include "llvm/ExecutionEngine/RTDyldMemoryManager.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/LegacyPassNameParser.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" +#include "llvm/Pass.h" #include "llvm/PassRegistry.h" -#include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetMachine.h" - -#include +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/IPO.h" using namespace llvm; +static cl::list +PassList(cl::desc("Optimizations available:")); + +static std::string OptIR; + +// Helper function to parse command line args and find the optimization level static void getOptLevel(const std::vector &ExtraArgs, CodeGenOpt::Level &OLvl) { // Find the optimization level from the command line args @@ -53,59 +73,221 @@ } } -void clang_fuzzer::HandleLLVM(const std::string &S, - const std::vector &ExtraArgs) { +// Helper function to call pass initialization functions +void InitEverything() { + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeCore(Registry); + initializeScalarOpts(Registry); + initializeVectorization(Registry); + initializeIPO(Registry); + initializeAnalysis(Registry); + initializeTransformUtils(Registry); + initializeInstCombine(Registry); + initializeAggressiveInstCombine(Registry); + initializeInstrumentation(Registry); + initializeTarget(Registry); +} + +// Helper function to add optimization passes to the TargetMachine at the +// specified optimization level, OptLevel +static void AddOptimizationPasses(legacy::PassManagerBase &MPM, + legacy::FunctionPassManager &FPM, + unsigned OptLevel, unsigned SizeLevel) { + // Verify that input is correct by adding a verifier pass + FPM.add(createVerifierPass()); + + // Create and initializa a PassManagerBuilder + PassManagerBuilder Builder; + Builder.OptLevel = OptLevel; + Builder.SizeLevel = SizeLevel; + Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false); + Builder.LoopVectorize = true; + Builder.populateFunctionPassManager(FPM); + Builder.populateModulePassManager(MPM); +} + +// Mimics the opt tool to run an optimization pass over the provided IR +void OptLLVM(const std::string IR, CodeGenOpt::Level &OLvl) { + InitEverything(); + + // Mimic argc and argv and pass them to ParseCommandLineOptions to initilize + // PassList, ie which optimizations we want to run on the IR + // TODO: Find a better way of doing this + char *args[2]; + char t[18] = "llvm-proto-fuzzer"; + char s[16] = "-loop-vectorize"; + args[0] = t; + args[1] = s; + cl::ParseCommandLineOptions(2, args, ""); + + // Create a module that will run the optimization passes + SMDiagnostic Err; + LLVMContext Context; + std::unique_ptr M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context); + if (!M || verifyModule(*M, &errs())) { + errs() << "error: could not parse IR!\n"; + std::exit(1); + } + + Triple ModuleTriple(M->getTargetTriple()); + TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + std::string CPUStr; + std::string FeaturesStr; + TargetMachine *Machine = nullptr; + std::unique_ptr TM(Machine); + setFunctionAttributes(CPUStr, FeaturesStr, *M); + + legacy::PassManager Passes; + TargetLibraryInfoImpl TLII(ModuleTriple); + Passes.add(new TargetLibraryInfoWrapperPass(TLII)); + Passes.add(createTargetTransformInfoWrapperPass(TargetIRAnalysis())); + + std::unique_ptr FPasses; + FPasses.reset(new legacy::FunctionPassManager(M.get())); + FPasses->add(createTargetTransformInfoWrapperPass(TargetIRAnalysis())); + + AddOptimizationPasses(Passes, *FPasses, 3, 0); + const PassInfo *PassInf = PassList[0]; + Pass *P = nullptr; + + if (PassInf->getNormalCtor()) + P = PassInf->getNormalCtor()(); + else { + errs() << "cannot create pass: " << PassInf->getPassName() << "\n"; + std::exit(1); + } + + if (P) + Passes.add(P); + + if (FPasses) { + FPasses->doInitialization(); + for (Function &F : *M) + FPasses->run(F); + FPasses->doFinalization(); + } + Passes.add(createVerifierPass()); + + // Add a pass that writes the optimized IR to an output stream + std::string outString; + raw_string_ostream OS(outString); + Passes.add(createPrintModulePass(OS, "", false)); + + Passes.run(*M); + + // Save the resulting IR to OptIR + OptIR = outString; +} + +// Main fuzz target called by ExampleClangLLVMProtoFuzzer.cpp +// Mimics the lli tool to JIT the LLVM IR code and execute it +void clang_fuzzer::HandleLLVM(const std::string &IR, + const std::vector &ExtraArgs, + char *m1, char *m2) { // Parse ExtraArgs to set the optimization level CodeGenOpt::Level OLvl; getOptLevel(ExtraArgs, OLvl); + + // First we optimize the IR by running a loop vectorizer pass + OptLLVM(IR, OLvl); - // Set the Module to include the the IR code to be compiled + // Create and initialize two modules, one for the optimized code and one + // for the unoptimized code SMDiagnostic Err; - LLVMContext Context; - std::unique_ptr M = parseIR(MemoryBufferRef(S, "IR"), Err, Context); - if (!M) { + std::unique_ptr Owner1 = parseIR(MemoryBufferRef(OptIR, "IR"), Err, + Context); + std::unique_ptr Owner2 = parseIR(MemoryBufferRef(IR, "IR"), Err, + Context); + Module *M1 = Owner1.get(); + Module *M2 = Owner2.get(); + if (!M1 || !M2) { errs() << "error: could not parse IR!\n"; std::exit(1); } - // Create a new Target - std::string Error; - const Target *TheTarget = TargetRegistry::lookupTarget( - sys::getDefaultTargetTriple(), Error); - if (!TheTarget) { - errs() << Error; + // Create and initialize two EngineBuilders from the Modules made above + TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + + std::string ErrorMsg1; + EngineBuilder builder1(std::move(Owner1)); + builder1.setMArch(MArch); + builder1.setMCPU(getCPUStr()); + builder1.setMAttrs(getFeatureList()); + builder1.setErrorStr(&ErrorMsg1); + builder1.setEngineKind(EngineKind::JIT); + builder1.setUseOrcMCJITReplacement(false); + RTDyldMemoryManager *RTDyldMM1 = nullptr; + RTDyldMM1 = new SectionMemoryManager(); + builder1.setMCJITMemoryManager( + std::unique_ptr(RTDyldMM1)); + builder1.setOptLevel(OLvl); + builder1.setTargetOptions(Options); + + std::string ErrorMsg2; + EngineBuilder builder2(std::move(Owner2)); + builder2.setMArch(MArch); + builder2.setMCPU(getCPUStr()); + builder2.setMAttrs(getFeatureList()); + builder2.setErrorStr(&ErrorMsg1); + builder2.setEngineKind(EngineKind::JIT); + builder2.setUseOrcMCJITReplacement(false); + RTDyldMemoryManager *RTDyldMM2 = nullptr; + RTDyldMM2 = new SectionMemoryManager(); + builder2.setMCJITMemoryManager( + std::unique_ptr(RTDyldMM2)); + builder2.setOptLevel(CodeGenOpt::None); + builder2.setTargetOptions(Options); + + std::unique_ptr EE1(builder1.create()); + if (!EE1) { + if (!ErrorMsg1.empty()) + errs() << "error creating EE1: " << ErrorMsg1 << "\n"; + else + errs() << "unknown error creating EE1!\n"; std::exit(1); } - TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + std::unique_ptr EE2(builder2.create()); + if (!EE2) { + if (!ErrorMsg2.empty()) + errs() << "error creating EE2: " << ErrorMsg2 << "\n"; + else + errs() << "unknown error creating EE2!\n"; + std::exit(1); + } - // Create a new Machine - std::string CPUStr = getCPUStr(); - std::string FeaturesStr = getFeaturesStr(); - std::unique_ptr Target(TheTarget->createTargetMachine( - sys::getDefaultTargetTriple(), CPUStr, FeaturesStr, Options, - getRelocModel(), getCodeModel(), OLvl)); - - // Create a new PassManager - legacy::PassManager PM; - TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple())); - PM.add(new TargetLibraryInfoWrapperPass(TLII)); - M->setDataLayout(Target->createDataLayout()); - - // Make sure the Module has no errors - if (verifyModule(*M, &errs())) { - errs() << "error: input module is broken!\n"; + //Specify the functions we want to call from the IR + Function *EntryFunc1 = M1->getFunction("foo"); + Function *EntryFunc2 = M2->getFunction("foo"); + if (!EntryFunc1 || !EntryFunc2) { + errs() << '\'' << EntryFunc1 << "\' function not found in module.\n"; std::exit(1); - } + } - setFunctionAttributes(CPUStr, FeaturesStr, *M); - - raw_null_ostream OS; - Target->addPassesToEmitFile(PM, OS, nullptr, TargetMachine::CGFT_ObjectFile, - false); - PM.run(*M); + typedef void (*func)(int*, int*, int*, int); + EE1->finalizeObject(); + EE1->runStaticConstructorsDestructors(false); + func f1 = (func) EE1->getPointerToFunction(EntryFunc1); + + EE2->finalizeObject(); + EE2->runStaticConstructorsDestructors(false); + func f2 = (func) EE2->getPointerToFunction(EntryFunc2); + + // Clear instruction cache before code will be executed. + if(RTDyldMM1) + static_cast(RTDyldMM1)->invalidateInstructionCache(); + if(RTDyldMM2) + static_cast(RTDyldMM2)->invalidateInstructionCache(); + + // Define some dummy arrays to use an input for now + int a[] = {1}; + int b[] = {1}; + int c[] = {1}; + + (*f1)(a, b, c, 1); + (*f2)(a, b, c, 1); + return; } -