[llvm] r317883 - [llvm-opt-fuzzer] Introduce llvm-opt-fuzzer for fuzzing optimization passes

Igor Laevsky via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 10 04:19:08 PST 2017


Author: igor.laevsky
Date: Fri Nov 10 04:19:08 2017
New Revision: 317883

URL: http://llvm.org/viewvc/llvm-project?rev=317883&view=rev
Log:
[llvm-opt-fuzzer] Introduce llvm-opt-fuzzer for fuzzing optimization passes

This change adds generic fuzzing tools capable of running libFuzzer tests on
any optimization pass or combination of them.

Differential Revision: https://reviews.llvm.org/D39555


Added:
    llvm/trunk/tools/llvm-opt-fuzzer/
    llvm/trunk/tools/llvm-opt-fuzzer/CMakeLists.txt
    llvm/trunk/tools/llvm-opt-fuzzer/DummyOptFuzzer.cpp
    llvm/trunk/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp
Modified:
    llvm/trunk/docs/FuzzingLLVM.rst
    llvm/trunk/include/llvm/FuzzMutate/FuzzerCLI.h
    llvm/trunk/lib/FuzzMutate/FuzzerCLI.cpp

Modified: llvm/trunk/docs/FuzzingLLVM.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/FuzzingLLVM.rst?rev=317883&r1=317882&r2=317883&view=diff
==============================================================================
--- llvm/trunk/docs/FuzzingLLVM.rst (original)
+++ llvm/trunk/docs/FuzzingLLVM.rst Fri Nov 10 04:19:08 2017
@@ -100,6 +100,28 @@ mode, the same example could be run like
 
    % bin/llvm-isel-fuzzer--aarch64-O0-gisel <corpus-dir>
 
+llvm-opt-fuzzer
+---------------
+
+A |LLVM IR fuzzer| aimed at finding bugs in optimization passes.
+
+It receives optimzation pipeline and runs it for each fuzzer input.
+
+Interface of this fuzzer almost directly mirrors ``llvm-isel-fuzzer``. Both
+``mtriple`` and ``passes`` arguments are required. Passes are specified in a
+format suitable for the new pass manager.
+
+.. code-block:: shell
+
+   % bin/llvm-opt-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple x86_64 -passes instcombine
+
+Similarly to the ``llvm-isel-fuzzer`` arguments in some predefined configurations
+might be embedded directly into the binary file name:
+
+.. code-block:: shell
+
+   % bin/llvm-opt-fuzzer--x86_64-instcombine <corpus-dir>
+
 llvm-mc-assemble-fuzzer
 -----------------------
 

Modified: llvm/trunk/include/llvm/FuzzMutate/FuzzerCLI.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/FuzzMutate/FuzzerCLI.h?rev=317883&r1=317882&r2=317883&view=diff
==============================================================================
--- llvm/trunk/include/llvm/FuzzMutate/FuzzerCLI.h (original)
+++ llvm/trunk/include/llvm/FuzzMutate/FuzzerCLI.h Fri Nov 10 04:19:08 2017
@@ -36,6 +36,10 @@ void parseFuzzerCLOpts(int ArgC, char *A
 /// of passing in command line arguments in the normal way.
 void handleExecNameEncodedBEOpts(StringRef ExecName);
 
+/// Handle optimizer options which are encoded in the executable name.
+/// Same semantics as in 'handleExecNameEncodedBEOpts'.
+void handleExecNameEncodedOptimizerOpts(StringRef ExecName);
+
 using FuzzerTestFun = int (*)(const uint8_t *Data, size_t Size);
 using FuzzerInitFun = int (*)(int *argc, char ***argv);
 

Modified: llvm/trunk/lib/FuzzMutate/FuzzerCLI.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/FuzzMutate/FuzzerCLI.cpp?rev=317883&r1=317882&r2=317883&view=diff
==============================================================================
--- llvm/trunk/lib/FuzzMutate/FuzzerCLI.cpp (original)
+++ llvm/trunk/lib/FuzzMutate/FuzzerCLI.cpp Fri Nov 10 04:19:08 2017
@@ -67,6 +67,40 @@ void llvm::handleExecNameEncodedBEOpts(S
   cl::ParseCommandLineOptions(CLArgs.size(), CLArgs.data());
 }
 
+void llvm::handleExecNameEncodedOptimizerOpts(StringRef ExecName) {
+  // TODO: Refactor parts common with the 'handleExecNameEncodedBEOpts'
+  std::vector<std::string> Args{ExecName};
+
+  auto NameAndArgs = ExecName.split("--");
+  if (NameAndArgs.second.empty())
+    return;
+
+  SmallVector<StringRef, 4> Opts;
+  NameAndArgs.second.split(Opts, '-');
+  for (StringRef Opt : Opts) {
+    if (Opt.startswith("instcombine")) {
+      Args.push_back("-passes=instcombine");
+    } else if (Triple(Opt).getArch()) {
+      Args.push_back("-mtriple=" + Opt.str());
+    } else {
+      errs() << ExecName << ": Unknown option: " << Opt << ".\n";
+      exit(1);
+    }
+  }
+
+  errs() << NameAndArgs.first << ": Injected args:";
+  for (int I = 1, E = Args.size(); I < E; ++I)
+    errs() << " " << Args[I];
+  errs() << "\n";
+
+  std::vector<const char *> CLArgs;
+  CLArgs.reserve(Args.size());
+  for (std::string &S : Args)
+    CLArgs.push_back(S.c_str());
+
+  cl::ParseCommandLineOptions(CLArgs.size(), CLArgs.data());
+}
+
 int llvm::runFuzzerOnInputs(int ArgC, char *ArgV[], FuzzerTestFun TestOne,
                             FuzzerInitFun Init) {
   errs() << "*** This tool was not linked to libFuzzer.\n"

Added: llvm/trunk/tools/llvm-opt-fuzzer/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-opt-fuzzer/CMakeLists.txt?rev=317883&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-opt-fuzzer/CMakeLists.txt (added)
+++ llvm/trunk/tools/llvm-opt-fuzzer/CMakeLists.txt Fri Nov 10 04:19:08 2017
@@ -0,0 +1,24 @@
+set(LLVM_LINK_COMPONENTS
+  ${LLVM_TARGETS_TO_BUILD}
+  Analysis
+  BitWriter
+  CodeGen
+  Core
+  Coroutines
+  IPO
+  IRReader
+  InstCombine
+  Instrumentation
+  FuzzMutate
+  MC
+  ObjCARCOpts
+  ScalarOpts
+  Support
+  Target
+  TransformUtils
+  Vectorize
+  Passes
+)
+
+add_llvm_fuzzer(llvm-opt-fuzzer llvm-opt-fuzzer.cpp
+  DUMMY_MAIN DummyOptFuzzer.cpp)

Added: llvm/trunk/tools/llvm-opt-fuzzer/DummyOptFuzzer.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-opt-fuzzer/DummyOptFuzzer.cpp?rev=317883&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-opt-fuzzer/DummyOptFuzzer.cpp (added)
+++ llvm/trunk/tools/llvm-opt-fuzzer/DummyOptFuzzer.cpp Fri Nov 10 04:19:08 2017
@@ -0,0 +1,21 @@
+//===--- DummyOptFuzzer.cpp - Entry point to sanity check the fuzzer ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of main so we can build and test without linking libFuzzer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/FuzzMutate/FuzzerCLI.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv);
+int main(int argc, char *argv[]) {
+    return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput,
+                                   LLVMFuzzerInitialize);
+}

Added: llvm/trunk/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp?rev=317883&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp (added)
+++ llvm/trunk/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp Fri Nov 10 04:19:08 2017
@@ -0,0 +1,259 @@
+//===--- 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.h"
+#include "llvm/FuzzMutate/FuzzerCLI.h"
+#include "llvm/FuzzMutate/IRMutator.h"
+#include "llvm/FuzzMutate/Operations.h"
+#include "llvm/FuzzMutate/Random.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<std::string>
+    TargetTripleStr("mtriple", cl::desc("Override target triple for module"));
+
+// Passes to run for this fuzzer instance. Expects new pass manager syntax.
+static cl::opt<std::string> PassPipeline(
+    "passes",
+    cl::desc("A textual description of the pass pipeline for testing"));
+
+static std::unique_ptr<IRMutator> Mutator;
+static std::unique_ptr<TargetMachine> TM;
+
+// This function is mostly copied from the llvm-isel-fuzzer.
+// TODO: Move this into FuzzMutate library and reuse.
+static std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
+                                           LLVMContext &Context) {
+
+  if (Size <= 1)
+    // We get bogus data given an empty corpus - just create a new module.
+    return llvm::make_unique<Module>("M", Context);
+
+  auto Buffer = MemoryBuffer::getMemBuffer(
+      StringRef(reinterpret_cast<const char *>(Data), Size), "Fuzzer input",
+      /*RequiresNullTerminator=*/false);
+
+  SMDiagnostic Err;
+  auto M = parseBitcodeFile(Buffer->getMemBufferRef(), Context);
+  if (Error E = M.takeError()) {
+    errs() << toString(std::move(E)) << "\n";
+    return nullptr;
+  }
+  return std::move(M.get());
+}
+
+// This function is copied from the llvm-isel-fuzzer.
+// TODO: Move this into FuzzMutate library and reuse.
+static size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize) {
+  std::string Buf;
+  {
+    raw_string_ostream OS(Buf);
+    WriteBitcodeToFile(&M, OS);
+  }
+  if (Buf.size() > MaxSize)
+      return 0;
+  memcpy(Dest, Buf.data(), Buf.size());
+  return Buf.size();
+}
+
+std::unique_ptr<IRMutator> createOptMutator() {
+  std::vector<TypeGetter> Types{
+      Type::getInt1Ty,  Type::getInt8Ty,  Type::getInt16Ty, Type::getInt32Ty,
+      Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy};
+
+  std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
+  Strategies.push_back(
+      llvm::make_unique<InjectorIRStrategy>(
+          InjectorIRStrategy::getDefaultOps()));
+  Strategies.push_back(
+      llvm::make_unique<InstDeleterIRStrategy>());
+
+  return llvm::make_unique<IRMutator>(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 = parseModule(Data, Size, Context);
+  if (!M || verifyModule(*M, &errs())) {
+    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();
+  }
+#endif
+
+  return writeModule(*M, Data, MaxSize);
+}
+
+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 = parseModule(Data, Size, Context);
+  if (!M || verifyModule(*M, &errs())) {
+    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");
+
+  // 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);
+  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;
+}




More information about the llvm-commits mailing list