diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 |
commit | 486754660bb926339aefcf012a3f848592babb8b (patch) | |
tree | ecdbc446c9876f4f120f701c243373cd3cb43db3 /tools/clang-fuzzer | |
parent | 55e6d896ad333f07bb3b1ba487df214fc268a4ab (diff) |
Notes
Diffstat (limited to 'tools/clang-fuzzer')
23 files changed, 919 insertions, 36 deletions
diff --git a/tools/clang-fuzzer/CMakeLists.txt b/tools/clang-fuzzer/CMakeLists.txt index b351ec51652de..5f07e66fe6754 100644 --- a/tools/clang-fuzzer/CMakeLists.txt +++ b/tools/clang-fuzzer/CMakeLists.txt @@ -9,12 +9,13 @@ elseif(LLVM_USE_SANITIZE_COVERAGE) unset(DUMMY_MAIN) endif() -# Hack to bypass LLVM's cmake sources check and allow multiple libraries and -# executables from this directory. +# Needed by LLVM's CMake checks because this file defines multiple targets. set(LLVM_OPTIONAL_SOURCES ClangFuzzer.cpp DummyClangFuzzer.cpp ExampleClangProtoFuzzer.cpp + ExampleClangLoopProtoFuzzer.cpp + ExampleClangLLVMProtoFuzzer.cpp ) if(CLANG_ENABLE_PROTO_FUZZER) @@ -25,6 +26,7 @@ if(CLANG_ENABLE_PROTO_FUZZER) include_directories(${PROTOBUF_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS cxx_proto.proto) + protobuf_generate_cpp(LOOP_PROTO_SRCS LOOP_PROTO_HDRS cxx_loop_proto.proto) set(LLVM_OPTIONAL_SOURCES ${LLVM_OPTIONAL_SOURCES} ${PROTO_SRCS}) add_clang_library(clangCXXProto ${PROTO_SRCS} @@ -34,6 +36,14 @@ if(CLANG_ENABLE_PROTO_FUZZER) ${PROTOBUF_LIBRARIES} ) + add_clang_library(clangCXXLoopProto + ${LOOP_PROTO_SRCS} + ${LOOP_PROTO_HDRS} + + LINK_LIBS + ${PROTOBUF_LIBRARIES} + ) + # Build and include libprotobuf-mutator include(ProtobufMutator) include_directories(${ProtobufMutator_INCLUDE_DIRS}) @@ -41,24 +51,63 @@ if(CLANG_ENABLE_PROTO_FUZZER) # Build the protobuf->C++ translation library and driver. add_clang_subdirectory(proto-to-cxx) + # Build the protobuf->LLVM IR translation library and driver. + add_clang_subdirectory(proto-to-llvm) + + # Build the fuzzer initialization library. + add_clang_subdirectory(fuzzer-initialize) + # Build the protobuf fuzzer add_clang_executable(clang-proto-fuzzer ${DUMMY_MAIN} ExampleClangProtoFuzzer.cpp ) - target_link_libraries(clang-proto-fuzzer - PRIVATE + # Build the loop protobuf fuzzer + add_clang_executable(clang-loop-proto-fuzzer + ${DUMMY_MAIN} + ExampleClangLoopProtoFuzzer.cpp + ) + + # Build the llvm protobuf fuzzer + add_clang_executable(clang-llvm-proto-fuzzer + ${DUMMY_MAIN} + ExampleClangLLVMProtoFuzzer.cpp + ) + + set(COMMON_PROTO_FUZZ_LIBRARIES ${ProtobufMutator_LIBRARIES} ${PROTOBUF_LIBRARIES} ${LLVM_LIB_FUZZING_ENGINE} - clangCXXProto + clangFuzzerInitialize + ) + + target_link_libraries(clang-proto-fuzzer + PRIVATE + ${COMMON_PROTO_FUZZ_LIBRARIES} clangHandleCXX + clangCXXProto clangProtoToCXX ) + target_link_libraries(clang-loop-proto-fuzzer + PRIVATE + ${COMMON_PROTO_FUZZ_LIBRARIES} + clangHandleCXX + clangCXXLoopProto + clangLoopProtoToCXX + ) + target_link_libraries(clang-llvm-proto-fuzzer + PRIVATE + ${COMMON_PROTO_FUZZ_LIBRARIES} + clangHandleLLVM + clangCXXLoopProto + clangLoopProtoToLLVM + ) + endif() add_clang_subdirectory(handle-cxx) +add_clang_subdirectory(handle-llvm) add_clang_executable(clang-fuzzer EXCLUDE_FROM_ALL diff --git a/tools/clang-fuzzer/ClangFuzzer.cpp b/tools/clang-fuzzer/ClangFuzzer.cpp index 2d35fb7735f97..f169f58a39b6b 100644 --- a/tools/clang-fuzzer/ClangFuzzer.cpp +++ b/tools/clang-fuzzer/ClangFuzzer.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file implements a function that runs Clang on a single +/// This file implements a function that runs Clang on a single /// input. This function is then linked into the Fuzzer library. /// //===----------------------------------------------------------------------===// diff --git a/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp b/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp new file mode 100644 index 0000000000000..347ba1c320dfa --- /dev/null +++ b/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp @@ -0,0 +1,28 @@ +//===-- ExampleClangLLVMProtoFuzzer.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 function that compiles a single LLVM IR string as +/// input and uses libprotobuf-mutator to find new inputs. This function is +/// then linked into the Fuzzer library. +/// +//===----------------------------------------------------------------------===// + +#include "cxx_loop_proto.pb.h" +#include "fuzzer-initialize/fuzzer_initialize.h" +#include "handle-llvm/handle_llvm.h" +#include "proto-to-llvm/loop_proto_to_llvm.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +using namespace clang_fuzzer; + +DEFINE_BINARY_PROTO_FUZZER(const LoopFunction &input) { + auto S = LoopFunctionToLLVMString(input); + HandleLLVM(S, GetCLArgs()); +} diff --git a/tools/clang-fuzzer/ExampleClangLoopProtoFuzzer.cpp b/tools/clang-fuzzer/ExampleClangLoopProtoFuzzer.cpp new file mode 100644 index 0000000000000..3640be13fafd1 --- /dev/null +++ b/tools/clang-fuzzer/ExampleClangLoopProtoFuzzer.cpp @@ -0,0 +1,30 @@ +//===-- ExampleClangLoopProtoFuzzer.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 function that runs Clang on a single +/// input and uses libprotobuf-mutator to find new inputs. This function is +/// then linked into the Fuzzer library. This file differs from +/// ExampleClangProtoFuzzer in that it uses a different protobuf that includes +/// C++ code with a single for loop. +/// +//===----------------------------------------------------------------------===// + +#include "cxx_loop_proto.pb.h" +#include "fuzzer-initialize/fuzzer_initialize.h" +#include "handle-cxx/handle_cxx.h" +#include "proto-to-cxx/proto_to_cxx.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +using namespace clang_fuzzer; + +DEFINE_BINARY_PROTO_FUZZER(const LoopFunction &input) { + auto S = LoopFunctionToString(input); + HandleCXX(S, GetCLArgs()); +} diff --git a/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp b/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp index ab734e85b8571..159ded3ca1854 100644 --- a/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp +++ b/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file implements a function that runs Clang on a single +/// This file implements a function that runs Clang on a single /// input and uses libprotobuf-mutator to find new inputs. This function is /// then linked into the Fuzzer library. /// @@ -17,28 +17,12 @@ #include "cxx_proto.pb.h" #include "handle-cxx/handle_cxx.h" #include "proto-to-cxx/proto_to_cxx.h" - +#include "fuzzer-initialize/fuzzer_initialize.h" #include "src/libfuzzer/libfuzzer_macro.h" -#include <cstring> - using namespace clang_fuzzer; -static std::vector<const char *> CLArgs; - -extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { - 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; - } - } - return 0; -} - DEFINE_BINARY_PROTO_FUZZER(const Function& input) { auto S = FunctionToString(input); - HandleCXX(S, CLArgs); + HandleCXX(S, GetCLArgs()); } diff --git a/tools/clang-fuzzer/cxx_loop_proto.proto b/tools/clang-fuzzer/cxx_loop_proto.proto new file mode 100644 index 0000000000000..f2b47ed43d897 --- /dev/null +++ b/tools/clang-fuzzer/cxx_loop_proto.proto @@ -0,0 +1,80 @@ +//===-- cxx_loop_proto.proto - Protobuf description of C++ with for loops -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes a subset of C++ as a protobuf. It is used to +/// more easily find interesting inputs for fuzzing Clang. This subset +/// differs from the one defined in cxx_proto.proto by eliminating while +/// loops and conditionals. The goal is that the C++ code generated will be +/// more likely to stress the LLVM loop vectorizer. +/// +//===----------------------------------------------------------------------===// + +syntax = "proto2"; + +message Const { + required int32 val = 1; +} + +message VarRef { + // Add an enum for each array in function signature + enum Arr { + ARR_A = 0; + ARR_B = 1; + ARR_C = 2; + }; + required Arr arr = 1; +} + +message BinaryOp { + enum Op { + PLUS = 0; + MINUS = 1; + MUL = 2; + XOR = 3; + AND = 4; + OR = 5; + EQ = 6; + NE = 7; + LE = 8; + GE = 9; + LT = 10; + GT = 11; + }; + required Op op = 1; + required Rvalue left = 2; + required Rvalue right = 3; +} + +message Rvalue { + oneof rvalue_oneof { + Const cons = 1; + BinaryOp binop = 2; + VarRef varref = 3; + } +} + +message AssignmentStatement { + required VarRef varref = 1; + required Rvalue rvalue = 2; +} + +message Statement { + required AssignmentStatement assignment = 1; +} + +message StatementSeq { + repeated Statement statements = 1; +} + +message LoopFunction { + required StatementSeq statements = 1; +} + +package clang_fuzzer; diff --git a/tools/clang-fuzzer/cxx_proto.proto b/tools/clang-fuzzer/cxx_proto.proto index 714a29861e6a5..499101fc0fbd8 100644 --- a/tools/clang-fuzzer/cxx_proto.proto +++ b/tools/clang-fuzzer/cxx_proto.proto @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file describes a subset of C++ as a protobuf. It is used to +/// This file describes a subset of C++ as a protobuf. It is used to /// more easily find interesting inputs for fuzzing Clang. /// //===----------------------------------------------------------------------===// diff --git a/tools/clang-fuzzer/fuzzer-initialize/CMakeLists.txt b/tools/clang-fuzzer/fuzzer-initialize/CMakeLists.txt new file mode 100644 index 0000000000000..c149fb3d4b36c --- /dev/null +++ b/tools/clang-fuzzer/fuzzer-initialize/CMakeLists.txt @@ -0,0 +1,3 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Support) + +add_clang_library(clangFuzzerInitialize fuzzer_initialize.cpp) diff --git a/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp b/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp new file mode 100644 index 0000000000000..75bf22803bcea --- /dev/null +++ b/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp @@ -0,0 +1,65 @@ +//===-- 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 two 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. +/// +//===----------------------------------------------------------------------===// + +#include "fuzzer_initialize.h" + +#include "llvm/InitializePasses.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include <cstring> + +using namespace clang_fuzzer; +using namespace llvm; + + +namespace clang_fuzzer { + +static std::vector<const char *> CLArgs; + +const std::vector<const char *>& GetCLArgs() { + return CLArgs; +} + +} + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeCore(Registry); + initializeScalarOpts(Registry); + initializeVectorization(Registry); + initializeIPO(Registry); + initializeAnalysis(Registry); + initializeTransformUtils(Registry); + initializeInstCombine(Registry); + initializeAggressiveInstCombine(Registry); + initializeInstrumentation(Registry); + initializeTarget(Registry); + + 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; + } + } + return 0; +} diff --git a/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.h b/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.h new file mode 100644 index 0000000000000..83a5cf9dc571a --- /dev/null +++ b/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.h @@ -0,0 +1,19 @@ +//==-- fuzzer_initialize.h - Fuzz Clang ------------------------------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines a function that returns the command line arguments for a specific +// call to the fuzz target. +// +//===----------------------------------------------------------------------===// + +#include <vector> + +namespace clang_fuzzer { +const std::vector<const char *>& GetCLArgs(); +} diff --git a/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp b/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp index 312ab91e5fe21..4985fedbe1184 100644 --- a/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp +++ b/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp @@ -18,17 +18,11 @@ #include "clang/Lex/PreprocessorOptions.h" #include "clang/Tooling/Tooling.h" #include "llvm/Option/Option.h" -#include "llvm/Support/TargetSelect.h" using namespace clang; void clang_fuzzer::HandleCXX(const std::string &S, const std::vector<const char *> &ExtraArgs) { - llvm::InitializeAllTargets(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmPrinters(); - llvm::InitializeAllAsmParsers(); - llvm::opt::ArgStringList CC1Args; CC1Args.push_back("-cc1"); for (auto &A : ExtraArgs) diff --git a/tools/clang-fuzzer/handle-llvm/CMakeLists.txt b/tools/clang-fuzzer/handle-llvm/CMakeLists.txt new file mode 100644 index 0000000000000..47f9fdf68f409 --- /dev/null +++ b/tools/clang-fuzzer/handle-llvm/CMakeLists.txt @@ -0,0 +1,30 @@ +set(LLVM_LINK_COMPONENTS + Analysis + CodeGen + Core + ExecutionEngine + IPO + IRReader + MC + MCJIT + Object + RuntimeDyld + SelectionDAG + Support + Target + TransformUtils + native +) + +# Depend on LLVM IR intrinsic generation. +set(handle_llvm_deps intrinsics_gen) +if (CLANG_BUILT_STANDALONE) + set(handle_llvm_deps) +endif() + +add_clang_library(clangHandleLLVM + handle_llvm.cpp + + DEPENDS + ${handle_llvm_deps} + ) diff --git a/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp b/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp new file mode 100644 index 0000000000000..ef544ae711aba --- /dev/null +++ b/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp @@ -0,0 +1,178 @@ +//==-- handle_llvm.cpp - Helper function for Clang fuzzers -----------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements HandleLLVM for use by the Clang fuzzers. First runs a 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. +// +//===----------------------------------------------------------------------===// + +#include "handle_llvm.h" + +#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/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/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Vectorize.h" + +using namespace llvm; + +// Helper function to parse command line args and find the optimization level +static void getOptLevel(const std::vector<const char *> &ExtraArgs, + CodeGenOpt::Level &OLvl) { + // Find the optimization level from the command line args + OLvl = CodeGenOpt::Default; + for (auto &A : ExtraArgs) { + if (A[0] == '-' && A[1] == 'O') { + switch(A[2]) { + case '0': OLvl = CodeGenOpt::None; break; + case '1': OLvl = CodeGenOpt::Less; break; + case '2': OLvl = CodeGenOpt::Default; break; + case '3': OLvl = CodeGenOpt::Aggressive; break; + default: + errs() << "error: opt level must be between 0 and 3.\n"; + std::exit(1); + } + } + } +} + +void ErrorAndExit(std::string message) { + errs()<< "ERROR: " << message << "\n"; + std::exit(1); +} + +// Helper function to add optimization passes to the TargetMachine at the +// specified optimization level, OptLevel +static void AddOptimizationPasses(legacy::PassManagerBase &MPM, + CodeGenOpt::Level OptLevel, + unsigned SizeLevel) { + // Create and initialize a PassManagerBuilder + PassManagerBuilder Builder; + Builder.OptLevel = OptLevel; + Builder.SizeLevel = SizeLevel; + Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false); + Builder.LoopVectorize = true; + Builder.populateModulePassManager(MPM); +} + +// Mimics the opt tool to run an optimization pass over the provided IR +std::string OptLLVM(const std::string &IR, CodeGenOpt::Level OLvl) { + // Create a module that will run the optimization passes + SMDiagnostic Err; + LLVMContext Context; + std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context); + if (!M || verifyModule(*M, &errs())) + ErrorAndExit("Could not parse IR"); + + setFunctionAttributes(getCPUStr(), getFeaturesStr(), *M); + + legacy::PassManager Passes; + Triple ModuleTriple(M->getTargetTriple()); + + Passes.add(new TargetLibraryInfoWrapperPass(ModuleTriple)); + Passes.add(createTargetTransformInfoWrapperPass(TargetIRAnalysis())); + Passes.add(createVerifierPass()); + + AddOptimizationPasses(Passes, OLvl, 0); + + // 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); + + return OS.str(); +} + +void CreateAndRunJITFun(const std::string &IR, CodeGenOpt::Level OLvl) { + SMDiagnostic Err; + LLVMContext Context; + std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, + Context); + if (!M) + ErrorAndExit("Could not parse IR"); + + Function *EntryFunc = M->getFunction("foo"); + if (!EntryFunc) + ErrorAndExit("Function not found in module"); + + std::string ErrorMsg; + EngineBuilder builder(std::move(M)); + builder.setMArch(MArch); + builder.setMCPU(getCPUStr()); + builder.setMAttrs(getFeatureList()); + builder.setErrorStr(&ErrorMsg); + builder.setEngineKind(EngineKind::JIT); + builder.setUseOrcMCJITReplacement(false); + builder.setMCJITMemoryManager(make_unique<SectionMemoryManager>()); + builder.setOptLevel(OLvl); + builder.setTargetOptions(InitTargetOptionsFromCodeGenFlags()); + + std::unique_ptr<ExecutionEngine> EE(builder.create()); + if (!EE) + ErrorAndExit("Could not create execution engine"); + + EE->finalizeObject(); + EE->runStaticConstructorsDestructors(false); + + typedef void (*func)(int*, int*, int*, int); + func f = reinterpret_cast<func>(EE->getPointerToFunction(EntryFunc)); + + // Define some dummy arrays to use an input for now + int a[] = {1}; + int b[] = {1}; + int c[] = {1}; + f(a, b, c, 1); + + EE->runStaticConstructorsDestructors(true); +} + +// 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<const char *> &ExtraArgs) { + // Parse ExtraArgs to set the optimization level + CodeGenOpt::Level OLvl; + getOptLevel(ExtraArgs, OLvl); + + // First we optimize the IR by running a loop vectorizer pass + std::string OptIR = OptLLVM(IR, OLvl); + + CreateAndRunJITFun(OptIR, OLvl); + CreateAndRunJITFun(IR, CodeGenOpt::None); + + return; +} diff --git a/tools/clang-fuzzer/handle-llvm/handle_llvm.h b/tools/clang-fuzzer/handle-llvm/handle_llvm.h new file mode 100644 index 0000000000000..38aec67994905 --- /dev/null +++ b/tools/clang-fuzzer/handle-llvm/handle_llvm.h @@ -0,0 +1,25 @@ +//==-- handle_llvm.h - Helper function for Clang fuzzers -------------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines HandleLLVM for use by the Clang fuzzers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_LLVM_HANDLELLVM_H +#define LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_LLVM_HANDLELLVM_H + +#include <string> +#include <vector> + +namespace clang_fuzzer { +void HandleLLVM(const std::string &S, + const std::vector<const char *> &ExtraArgs); +} // namespace clang_fuzzer + +#endif diff --git a/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt b/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt index 910b793e0e0d9..339959b81af0c 100644 --- a/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt +++ b/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt @@ -1,14 +1,22 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD}) set(CMAKE_CXX_FLAGS ${CXX_FLAGS_NOFUZZ}) -# Hack to bypass LLVM's CMake source checks so we can have both a library and -# an executable built from this directory. -set(LLVM_OPTIONAL_SOURCES proto_to_cxx.cpp proto_to_cxx_main.cpp) +# Needed by LLVM's CMake checks because this file defines multiple targets. +set(LLVM_OPTIONAL_SOURCES proto_to_cxx.cpp proto_to_cxx_main.cpp + loop_proto_to_cxx.cpp loop_proto_to_cxx_main.cpp) add_clang_library(clangProtoToCXX proto_to_cxx.cpp DEPENDS clangCXXProto LINK_LIBS clangCXXProto ${PROTOBUF_LIBRARIES} ) +add_clang_library(clangLoopProtoToCXX loop_proto_to_cxx.cpp + DEPENDS clangCXXLoopProto + LINK_LIBS clangCXXLoopProto ${PROTOBUF_LIBRARIES} + ) + add_clang_executable(clang-proto-to-cxx proto_to_cxx_main.cpp) +add_clang_executable(clang-loop-proto-to-cxx loop_proto_to_cxx_main.cpp) + target_link_libraries(clang-proto-to-cxx PRIVATE clangProtoToCXX) +target_link_libraries(clang-loop-proto-to-cxx PRIVATE clangLoopProtoToCXX) diff --git a/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx.cpp b/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx.cpp new file mode 100644 index 0000000000000..7d8f6650aadb4 --- /dev/null +++ b/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx.cpp @@ -0,0 +1,131 @@ +//==-- loop_proto_to_cxx.cpp - Protobuf-C++ conversion ---------------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements functions for converting between protobufs and C++. Differs from +// proto_to_cxx.cpp by wrapping all the generated C++ code in a single for +// loop. Also coutputs a different function signature that includes a +// size_t parameter for the loop to use. The C++ code generated is meant to +// stress the LLVM loop vectorizer. +// +// Still a work in progress. +// +//===----------------------------------------------------------------------===// + +#include "cxx_loop_proto.pb.h" +#include "proto_to_cxx.h" + +// The following is needed to convert protos in human-readable form +#include <google/protobuf/text_format.h> + +#include <ostream> +#include <sstream> + +namespace clang_fuzzer { + +// Forward decls. +std::ostream &operator<<(std::ostream &os, const BinaryOp &x); +std::ostream &operator<<(std::ostream &os, const StatementSeq &x); + +// Proto to C++. +std::ostream &operator<<(std::ostream &os, const Const &x) { + return os << "(" << x.val() << ")"; +} +std::ostream &operator<<(std::ostream &os, const VarRef &x) { + switch (x.arr()) { + case VarRef::ARR_A: + return os << "a[i]"; + case VarRef::ARR_B: + return os << "b[i]"; + case VarRef::ARR_C: + return os << "c[i]"; + } +} +std::ostream &operator<<(std::ostream &os, const Rvalue &x) { + if (x.has_cons()) + return os << x.cons(); + if (x.has_binop()) + return os << x.binop(); + if (x.has_varref()) + return os << x.varref(); + return os << "1"; +} +std::ostream &operator<<(std::ostream &os, const BinaryOp &x) { + os << "(" << x.left(); + switch (x.op()) { + case BinaryOp::PLUS: + os << "+"; + break; + case BinaryOp::MINUS: + os << "-"; + break; + case BinaryOp::MUL: + os << "*"; + break; + case BinaryOp::XOR: + os << "^"; + break; + case BinaryOp::AND: + os << "&"; + break; + case BinaryOp::OR: + os << "|"; + break; + case BinaryOp::EQ: + os << "=="; + break; + case BinaryOp::NE: + os << "!="; + break; + case BinaryOp::LE: + os << "<="; + break; + case BinaryOp::GE: + os << ">="; + break; + case BinaryOp::LT: + os << "<"; + break; + case BinaryOp::GT: + os << ">"; + break; + } + return os << x.right() << ")"; +} +std::ostream &operator<<(std::ostream &os, const AssignmentStatement &x) { + return os << x.varref() << "=" << x.rvalue() << ";\n"; +} +std::ostream &operator<<(std::ostream &os, const Statement &x) { + return os << x.assignment(); +} +std::ostream &operator<<(std::ostream &os, const StatementSeq &x) { + for (auto &st : x.statements()) + os << st; + return os; +} +std::ostream &operator<<(std::ostream &os, const LoopFunction &x) { + return os << "void foo(int *a, int *b, int *__restrict__ c, size_t s) {\n" + << "for (int i=0; i<s; i++){\n" + << x.statements() << "}\n}\n"; +} + +// --------------------------------- + +std::string LoopFunctionToString(const LoopFunction &input) { + std::ostringstream os; + os << input; + return os.str(); +} +std::string LoopProtoToCxx(const uint8_t *data, size_t size) { + LoopFunction message; + if (!message.ParsePartialFromArray(data, size)) + return "#error invalid proto\n"; + return LoopFunctionToString(message); +} + +} // namespace clang_fuzzer diff --git a/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx_main.cpp b/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx_main.cpp new file mode 100644 index 0000000000000..a4b8e58c12737 --- /dev/null +++ b/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx_main.cpp @@ -0,0 +1,31 @@ +//==-- loop_proto_to_cxx_main.cpp - Driver for protobuf-C++ conversion -----==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements a simple driver to print a C++ program from a protobuf with loops. +// +//===----------------------------------------------------------------------===// + + +#include <fstream> +#include <iostream> +#include <streambuf> +#include <string> + +#include "proto_to_cxx.h" + +int main(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + std::fstream in(argv[i]); + std::string str((std::istreambuf_iterator<char>(in)), + std::istreambuf_iterator<char>()); + std::cout << "// " << argv[i] << std::endl; + std::cout << clang_fuzzer::LoopProtoToCxx( + reinterpret_cast<const uint8_t *>(str.data()), str.size()); + } +} diff --git a/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp index cd75e0c99c47a..4a86515f55986 100644 --- a/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp +++ b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp @@ -94,7 +94,7 @@ std::string FunctionToString(const Function &input) { } std::string ProtoToCxx(const uint8_t *data, size_t size) { Function message; - if (!message.ParseFromArray(data, size)) + if (!message.ParsePartialFromArray(data, size)) return "#error invalid proto\n"; return FunctionToString(message); } diff --git a/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h index 1985e91ba2cdd..8d2e2e6f00819 100644 --- a/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h +++ b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h @@ -17,6 +17,10 @@ namespace clang_fuzzer { class Function; +class LoopFunction; + std::string FunctionToString(const Function &input); std::string ProtoToCxx(const uint8_t *data, size_t size); +std::string LoopFunctionToString(const LoopFunction &input); +std::string LoopProtoToCxx(const uint8_t *data, size_t size); } diff --git a/tools/clang-fuzzer/proto-to-llvm/CMakeLists.txt b/tools/clang-fuzzer/proto-to-llvm/CMakeLists.txt new file mode 100644 index 0000000000000..ae58523f2274f --- /dev/null +++ b/tools/clang-fuzzer/proto-to-llvm/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD}) +set(CMAKE_CXX_FLAGS ${CXX_FLAGS_NOFUZZ}) + +# Needed by LLVM's CMake checks because this file defines multiple targets. +set(LLVM_OPTIONAL_SOURCES loop_proto_to_llvm.cpp loop_proto_to_llvm_main.cpp) + +add_clang_library(clangLoopProtoToLLVM loop_proto_to_llvm.cpp + DEPENDS clangCXXLoopProto + LINK_LIBS clangCXXLoopProto ${PROTOBUF_LIBRARIES} + ) + +add_clang_executable(clang-loop-proto-to-llvm loop_proto_to_llvm_main.cpp) + +target_link_libraries(clang-loop-proto-to-llvm PRIVATE clangLoopProtoToLLVM) diff --git a/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.cpp b/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.cpp new file mode 100644 index 0000000000000..16dbcb7b49b2b --- /dev/null +++ b/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.cpp @@ -0,0 +1,156 @@ +//==-- loop_proto_to_llvm.cpp - Protobuf-C++ conversion +//---------------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements functions for converting between protobufs and LLVM IR. +// +// +//===----------------------------------------------------------------------===// + +#include "loop_proto_to_llvm.h" +#include "cxx_loop_proto.pb.h" + +// The following is needed to convert protos in human-readable form +#include <google/protobuf/text_format.h> + +#include <ostream> +#include <sstream> + +namespace clang_fuzzer { + +// Forward decls +std::string BinopToString(std::ostream &os, const BinaryOp &x); +std::string StateSeqToString(std::ostream &os, const StatementSeq &x); + +// Counter variable to generate new LLVM IR variable names and wrapper function +std::string get_var() { + static int ctr = 0; + return "%var" + std::to_string(ctr++); +} + +// Proto to LLVM. + +std::string ConstToString(const Const &x) { + return std::to_string(x.val()); +} +std::string VarRefToString(std::ostream &os, const VarRef &x) { + std::string arr; + switch(x.arr()) { + case VarRef::ARR_A: + arr = "%a"; + break; + case VarRef::ARR_B: + arr = "%b"; + break; + case VarRef::ARR_C: + arr = "%c"; + break; + } + std::string ptr_var = get_var(); + os << ptr_var << " = getelementptr i32, i32* " << arr << ", i64 %ct\n"; + return ptr_var; +} +std::string RvalueToString(std::ostream &os, const Rvalue &x) { + if(x.has_cons()) + return ConstToString(x.cons()); + if(x.has_binop()) + return BinopToString(os, x.binop()); + if(x.has_varref()) { + std::string var_ref = VarRefToString(os, x.varref()); + std::string val_var = get_var(); + os << val_var << " = load i32, i32* " << var_ref << "\n"; + return val_var; + } + return "1"; + +} +std::string BinopToString(std::ostream &os, const BinaryOp &x) { + std::string left = RvalueToString(os, x.left()); + std::string right = RvalueToString(os, x.right()); + std::string op; + switch (x.op()) { + case BinaryOp::PLUS: + op = "add"; + break; + case BinaryOp::MINUS: + op = "sub"; + break; + case BinaryOp::MUL: + op = "mul"; + break; + case BinaryOp::XOR: + op = "xor"; + break; + case BinaryOp::AND: + op = "and"; + break; + case BinaryOp::OR: + op = "or"; + break; + // Support for Boolean operators will be added later + case BinaryOp::EQ: + case BinaryOp::NE: + case BinaryOp::LE: + case BinaryOp::GE: + case BinaryOp::LT: + case BinaryOp::GT: + op = "add"; + break; + } + std::string val_var = get_var(); + os << val_var << " = " << op << " i32 " << left << ", " << right << "\n"; + return val_var; +} +std::ostream &operator<<(std::ostream &os, const AssignmentStatement &x) { + std::string rvalue = RvalueToString(os, x.rvalue()); + std::string var_ref = VarRefToString(os, x.varref()); + return os << "store i32 " << rvalue << ", i32* " << var_ref << "\n"; +} +std::ostream &operator<<(std::ostream &os, const Statement &x) { + return os << x.assignment(); +} +std::ostream &operator<<(std::ostream &os, const StatementSeq &x) { + for (auto &st : x.statements()) { + os << st; + } + return os; +} +std::ostream &operator<<(std::ostream &os, const LoopFunction &x) { + return os << "define void @foo(i32* %a, i32* %b, i32* noalias %c, i64 %s) {\n" + << "%i = alloca i64\n" + << "store i64 0, i64* %i\n" + << "br label %loop\n\n" + << "loop:\n" + << "%ct = load i64, i64* %i\n" + << "%comp = icmp eq i64 %ct, %s\n" + << "br i1 %comp, label %endloop, label %body\n\n" + << "body:\n" + << x.statements() + << "%z = add i64 1, %ct\n" + << "store i64 %z, i64* %i\n" + << "br label %loop\n\n" + << "endloop:\n" + << "ret void\n}\n"; +} + +// --------------------------------- + +std::string LoopFunctionToLLVMString(const LoopFunction &input) { + std::ostringstream os; + os << input; + return os.str(); +} +std::string LoopProtoToLLVM(const uint8_t *data, size_t size) { + LoopFunction message; + if (!message.ParsePartialFromArray(data, size)) + return "#error invalid proto\n"; + return LoopFunctionToLLVMString(message); +} + +} // namespace clang_fuzzer diff --git a/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.h b/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.h new file mode 100644 index 0000000000000..51660fcb710b2 --- /dev/null +++ b/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.h @@ -0,0 +1,23 @@ +//==-- loop_proto_to_llvm.h - Protobuf-C++ conversion ----------------------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines functions for converting between protobufs and LLVM IR. +// +//===----------------------------------------------------------------------===// + +#include <cstdint> +#include <cstddef> +#include <string> + +namespace clang_fuzzer { +class LoopFunction; + +std::string LoopFunctionToLLVMString(const LoopFunction &input); +std::string LoopProtoToLLVM(const uint8_t *data, size_t size); +} diff --git a/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm_main.cpp b/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm_main.cpp new file mode 100644 index 0000000000000..17ca15ec27f54 --- /dev/null +++ b/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm_main.cpp @@ -0,0 +1,31 @@ +//==-- loop_proto_to_llvm_main.cpp - Driver for protobuf-LLVM conversion----==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements a simple driver to print a LLVM program from a protobuf with loops +// +//===----------------------------------------------------------------------===// + + +#include <fstream> +#include <iostream> +#include <streambuf> +#include <string> + +#include "loop_proto_to_llvm.h" + +int main(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + std::fstream in(argv[i]); + std::string str((std::istreambuf_iterator<char>(in)), + std::istreambuf_iterator<char>()); + std::cout << ";; " << argv[i] << std::endl; + std::cout << clang_fuzzer::LoopProtoToLLVM( + reinterpret_cast<const uint8_t *>(str.data()), str.size()); + } +} |