[lib/Fuzzer] extend the fuzzer interface to allow user-supplied mutators
authorKostya Serebryany <kcc@google.com>
Fri, 22 May 2015 22:35:31 +0000 (22:35 +0000)
committerKostya Serebryany <kcc@google.com>
Fri, 22 May 2015 22:35:31 +0000 (22:35 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@238059 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
lib/Fuzzer/CMakeLists.txt
lib/Fuzzer/FuzzerCrossOver.cpp
lib/Fuzzer/FuzzerDriver.cpp
lib/Fuzzer/FuzzerInterface.cpp [new file with mode: 0644]
lib/Fuzzer/FuzzerInterface.h
lib/Fuzzer/FuzzerInternal.h
lib/Fuzzer/FuzzerLoop.cpp
lib/Fuzzer/FuzzerMutate.cpp
lib/Fuzzer/test/CMakeLists.txt
lib/Fuzzer/test/FuzzerUnittest.cpp
lib/Fuzzer/test/UserSuppliedFuzzerTest.cpp [new file with mode: 0644]
lib/Fuzzer/test/fuzzer.test

index 49d7f8f..8b4d619 100644 (file)
@@ -2,8 +2,9 @@ set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS_RELEASE}")
 # Disable the coverage and sanitizer instrumentation for the fuzzer itself.
 set(CMAKE_CXX_FLAGS_RELEASE "${LIBFUZZER_FLAGS_BASE} -O2 -fno-sanitize=all")
 if( LLVM_USE_SANITIZE_COVERAGE )
-  add_library(LLVMFuzzerNoMain OBJECT
+  add_library(LLVMFuzzerNoMainObjects OBJECT
     FuzzerCrossOver.cpp
+    FuzzerInterface.cpp
     FuzzerTraceState.cpp
     FuzzerDriver.cpp
     FuzzerIO.cpp
@@ -13,9 +14,12 @@ if( LLVM_USE_SANITIZE_COVERAGE )
     FuzzerSHA1.cpp
     FuzzerUtil.cpp
     )
+  add_library(LLVMFuzzerNoMain STATIC
+    $<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
+    )
   add_library(LLVMFuzzer STATIC
     FuzzerMain.cpp
-    $<TARGET_OBJECTS:LLVMFuzzerNoMain>
+    $<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
     )
 
   if( LLVM_INCLUDE_TESTS )
index 94af6d5..f03a94a 100644 (file)
@@ -9,39 +9,42 @@
 // Cross over test inputs.
 //===----------------------------------------------------------------------===//
 
+#include <cstring>
+
 #include "FuzzerInternal.h"
-#include <algorithm>
 
 namespace fuzzer {
 
-// Cross A and B, store the result (ap to MaxLen bytes) in U.
-void CrossOver(const Unit &A, const Unit &B, Unit *U, size_t MaxLen) {
-  size_t Size = rand() % MaxLen + 1;
-  U->clear();
-  const Unit *V = &A;
-  size_t PosA = 0;
-  size_t PosB = 0;
-  size_t *Pos = &PosA;
-  while (U->size() < Size && (PosA < A.size() || PosB < B.size())) {
-    // Merge a part of V into U.
-    size_t SizeLeftU = Size - U->size();
-    if (*Pos < V->size()) {
-      size_t SizeLeftV = V->size() - *Pos;
-      size_t MaxExtraSize = std::min(SizeLeftU, SizeLeftV);
+// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
+size_t CrossOver(const uint8_t *Data1, size_t Size1,
+                 const uint8_t *Data2, size_t Size2,
+                 uint8_t *Out, size_t MaxOutSize) {
+  MaxOutSize = rand() % MaxOutSize + 1;
+  size_t OutPos = 0;
+  size_t Pos1 = 0;
+  size_t Pos2 = 0;
+  size_t *InPos = &Pos1;
+  size_t InSize = Size1;
+  const uint8_t *Data = Data1;
+  bool CurrentlyUsingFirstData = true;
+  while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) {
+    // Merge a part of Data into Out.
+    size_t OutSizeLeft = MaxOutSize - OutPos;
+    if (*InPos < InSize) {
+      size_t InSizeLeft = InSize - *InPos;
+      size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft);
       size_t ExtraSize = rand() % MaxExtraSize + 1;
-      U->insert(U->end(), V->begin() + *Pos, V->begin() + *Pos + ExtraSize);
-      (*Pos) += ExtraSize;
-    }
-
-    // Use the other Unit on the next iteration.
-    if (Pos == &PosA) {
-      Pos = &PosB;
-      V = &B;
-    } else {
-      Pos = &PosA;
-      V = &A;
+      memcpy(Out + OutPos, Data + *InPos, ExtraSize);
+      OutPos += ExtraSize;
+      (*InPos) += ExtraSize;
     }
+    // Use the other input data on the next iteration.
+    InPos  = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
+    InSize = CurrentlyUsingFirstData ? Size2 : Size1;
+    Data   = CurrentlyUsingFirstData ? Data2 : Data1;
+    CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
   }
+  return OutPos;
 }
 
 }  // namespace fuzzer
index edfe09e..780b615 100644 (file)
@@ -204,6 +204,11 @@ int ApplyTokens(const Fuzzer &F, const char *InputFilePath) {
 }
 
 int FuzzerDriver(int argc, char **argv, UserCallback Callback) {
+  SimpleUserSuppliedFuzzer SUSF(Callback);
+  return FuzzerDriver(argc, argv, SUSF);
+}
+
+int FuzzerDriver(int argc, char **argv, UserSuppliedFuzzer &USF) {
   using namespace fuzzer;
 
   ProgName = argv[0];
@@ -244,7 +249,7 @@ int FuzzerDriver(int argc, char **argv, UserCallback Callback) {
   if (Flags.sync_command)
     Options.SyncCommand = Flags.sync_command;
   Options.SyncTimeout = Flags.sync_timeout;
-  Fuzzer F(Callback, Options);
+  Fuzzer F(USF, Options);
 
   unsigned seed = Flags.seed;
   // Initialize seed.
diff --git a/lib/Fuzzer/FuzzerInterface.cpp b/lib/Fuzzer/FuzzerInterface.cpp
new file mode 100644 (file)
index 0000000..dcd4e74
--- /dev/null
@@ -0,0 +1,27 @@
+//===- FuzzerInterface.cpp - Mutate a test input --------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Parts of public interface for libFuzzer.
+//===----------------------------------------------------------------------===//
+
+
+#include "FuzzerInterface.h"
+#include "FuzzerInternal.h"
+
+namespace fuzzer {
+size_t UserSuppliedFuzzer::BasicMutate(uint8_t *Data, size_t Size,
+                                       size_t MaxSize) {
+  return ::fuzzer::Mutate(Data, Size, MaxSize);
+}
+size_t UserSuppliedFuzzer::BasicCrossOver(const uint8_t *Data1, size_t Size1,
+                                          const uint8_t *Data2, size_t Size2,
+                                          uint8_t *Out, size_t MaxOutSize) {
+  return ::fuzzer::CrossOver(Data1, Size1, Data2, Size2, Out, MaxOutSize);
+}
+
+}  // namespace fuzzer.
index 49d8c0f..d4b0f99 100644 (file)
@@ -9,6 +9,10 @@
 // Define the interface between the Fuzzer and the library being tested.
 //===----------------------------------------------------------------------===//
 
+// WARNING: keep the interface free of STL or any other header-based C++ lib,
+// to avoid bad interactions between the code used in the fuzzer and
+// the code used in the target function.
+
 #ifndef LLVM_FUZZER_INTERFACE_H
 #define LLVM_FUZZER_INTERFACE_H
 
 
 namespace fuzzer {
 
-typedef void (*UserCallback)(const uint8_t *data, size_t size);
+// Simple C-like interface with a single user-supplied callback.
+/* Usage: ---------------------------------------------------------------------
+#include "FuzzerInterface.h"
+
+void LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  DoStuffWithData(Data, Size);
+}
+
+// Implement your own main() or use the one from FuzzerMain.cpp.
+int main(int argc, char **argv) {
+  InitializeMeIfNeeded();
+  return fuzzer::FuzzerDriver(argc, argv, LLVMFuzzerTestOneInput);
+}
+----------------------------------------------------------------------------- */
+typedef void (*UserCallback)(const uint8_t *Data, size_t Size);
 int FuzzerDriver(int argc, char **argv, UserCallback Callback);
 
+// An abstract class that allows to use user-supplied mutators with libFuzzer.
+/* Usage: ---------------------------------------------------------------------
+#include "FuzzerInterface.h"
+class MyFuzzer : public fuzzer::UserSuppliedFuzzer {
+ public:
+  // Must define the target function.
+  void TargetFunction(...) { ... }
+  // Optionally define the mutator.
+  size_t Mutate(...) { ... }
+  // Optionally define the CrossOver method.
+  size_t CrossOver(...) { ... }
+};
+
+int main(int argc, char **argv) {
+  MyFuzzer F;
+  fuzzer::FuzzerDriver(argc, argv, F);
+}
+----------------------------------------------------------------------------- */
+class UserSuppliedFuzzer {
+ public:
+  // Executes the target function on 'Size' bytes of 'Data'.
+  virtual void TargetFunction(const uint8_t *Data, size_t Size) = 0;
+  // Mutates 'Size' bytes of data in 'Data' inplace into up to 'MaxSize' bytes,
+  // returns the new size of the data.
+  virtual size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+    return BasicMutate(Data, Size, MaxSize);
+  }
+  // Crosses 'Data1' and 'Data2', writes up to 'MaxOutSize' bytes into Out,
+  // returns the number of bytes written.
+  virtual size_t CrossOver(const uint8_t *Data1, size_t Size1,
+                           const uint8_t *Data2, size_t Size2,
+                           uint8_t *Out, size_t MaxOutSize) {
+    return BasicCrossOver(Data1, Size1, Data2, Size2, Out, MaxOutSize);
+  }
+  virtual ~UserSuppliedFuzzer() {}
+
+ protected:
+  // These can be called internally by Mutate and CrossOver.
+  size_t BasicMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+  size_t BasicCrossOver(const uint8_t *Data1, size_t Size1,
+                        const uint8_t *Data2, size_t Size2,
+                        uint8_t *Out, size_t MaxOutSize);
+};
+
+int FuzzerDriver(int argc, char **argv, UserSuppliedFuzzer &USF);
+
 }  // namespace fuzzer
 
 #endif  // LLVM_FUZZER_INTERFACE_H
index b0c27b0..b25449b 100644 (file)
@@ -33,9 +33,10 @@ void CopyFileToErr(const std::string &Path);
 std::string DirPlusFile(const std::string &DirPath,
                         const std::string &FileName);
 
-void Mutate(Unit *U, size_t MaxLen);
+size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
 
-void CrossOver(const Unit &A, const Unit &B, Unit *U, size_t MaxLen);
+size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
+                 size_t Size2, uint8_t *Out, size_t MaxOutSize);
 
 void Print(const Unit &U, const char *PrintAfter = "");
 void PrintASCII(const Unit &U, const char *PrintAfter = "");
@@ -72,7 +73,7 @@ class Fuzzer {
     std::string SyncCommand;
     std::vector<std::string> Tokens;
   };
-  Fuzzer(UserCallback Callback, FuzzingOptions Options);
+  Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options);
   void AddToCorpus(const Unit &U) { Corpus.push_back(U); }
   void Loop(size_t NumIterations);
   void ShuffleAndMinimize();
@@ -144,7 +145,7 @@ class Fuzzer {
     return Res;
   }
 
-  UserCallback Callback;
+  UserSuppliedFuzzer &USF;
   FuzzingOptions Options;
   system_clock::time_point ProcessStartTime = system_clock::now();
   system_clock::time_point LastExternalSync = system_clock::now();
@@ -153,4 +154,15 @@ class Fuzzer {
   long EpochOfLastReadOfOutputCorpus = 0;
 };
 
+class SimpleUserSuppliedFuzzer: public UserSuppliedFuzzer {
+ public:
+  SimpleUserSuppliedFuzzer(UserCallback Callback) : Callback(Callback) {}
+  virtual void TargetFunction(const uint8_t *Data, size_t Size) {
+    return Callback(Data, Size);
+  }
+
+ private:
+  UserCallback Callback;
+};
+
 };  // namespace fuzzer
index 696811b..2e4da1d 100644 (file)
@@ -19,8 +19,8 @@ namespace fuzzer {
 // Only one Fuzzer per process.
 static Fuzzer *F;
 
-Fuzzer::Fuzzer(UserCallback Callback, FuzzingOptions Options)
-    : Callback(Callback), Options(Options) {
+Fuzzer::Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options)
+    : USF(USF), Options(Options) {
   SetDeathCallback();
   InitializeTraceState();
   assert(!F);
@@ -207,10 +207,10 @@ Unit Fuzzer::SubstituteTokens(const Unit &U) const {
 
 void Fuzzer::ExecuteCallback(const Unit &U) {
   if (Options.Tokens.empty()) {
-    Callback(U.data(), U.size());
+    USF.TargetFunction(U.data(), U.size());
   } else {
     auto T = SubstituteTokens(U);
-    Callback(T.data(), T.size());
+    USF.TargetFunction(T.data(), T.size());
   }
 }
 
@@ -321,7 +321,11 @@ void Fuzzer::ReportNewCoverage(size_t NewCoverage, const Unit &U) {
 void Fuzzer::MutateAndTestOne(Unit *U) {
   for (int i = 0; i < Options.MutateDepth; i++) {
     StartTraceRecording();
-    Mutate(U, Options.MaxLen);
+    size_t Size = U->size();
+    U->resize(Options.MaxLen);
+    size_t NewSize = USF.Mutate(U->data(), Size, U->size());
+    assert(NewSize > 0 && NewSize <= Options.MaxLen);
+    U->resize(NewSize);
     RunOneAndUpdateCorpus(*U);
     size_t NumTraceBasedMutations = StopTraceRecording();
     for (size_t j = 0; j < NumTraceBasedMutations; j++) {
@@ -344,8 +348,12 @@ void Fuzzer::Loop(size_t NumIterations) {
       // Now, cross with others.
       if (Options.DoCrossOver) {
         for (size_t J2 = 0; J2 < Corpus.size(); J2++) {
-          CurrentUnit.clear();
-          CrossOver(Corpus[J1], Corpus[J2], &CurrentUnit, Options.MaxLen);
+          CurrentUnit.resize(Options.MaxLen);
+          size_t NewSize = USF.CrossOver(
+              Corpus[J1].data(), Corpus[J1].size(), Corpus[J2].data(),
+              Corpus[J2].size(), CurrentUnit.data(), CurrentUnit.size());
+          assert(NewSize > 0 && NewSize <= Options.MaxLen);
+          CurrentUnit.resize(NewSize);
           MutateAndTestOne(&CurrentUnit);
         }
       }
index b28264a..f537fa9 100644 (file)
@@ -9,6 +9,8 @@
 // Mutate a test input.
 //===----------------------------------------------------------------------===//
 
+#include <cstring>
+
 #include "FuzzerInternal.h"
 
 namespace fuzzer {
@@ -31,40 +33,39 @@ static char RandCh() {
   return Special[rand() % (sizeof(Special) - 1)];
 }
 
-// Mutate U in place.
-void Mutate(Unit *U, size_t MaxLen) {
-  assert(MaxLen > 0);
-  assert(U->size() <= MaxLen);
-  if (U->empty()) {
-    for (size_t i = 0; i < MaxLen; i++)
-      U->push_back(RandCh());
-    return;
+// Mutates Data in place, returns new size.
+size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+  assert(MaxSize > 0);
+  assert(Size <= MaxSize);
+  if (Size == 0) {
+    for (size_t i = 0; i < MaxSize; i++)
+      Data[i] = RandCh();
+    return MaxSize;
   }
-  assert(!U->empty());
+  assert(Size > 0);
+  size_t Idx = rand() % Size;
   switch (rand() % 3) {
   case 0:
-    if (U->size() > 1) {
-      U->erase(U->begin() + rand() % U->size());
-      break;
+    if (Size > 1) {
+      // Erase Data[Idx].
+      memmove(Data + Idx, Data + Idx + 1, Size - Idx - 1);
+      Size = Size - 1;
     }
     [[clang::fallthrough]];
   case 1:
-    if (U->size() < MaxLen) {
-      U->insert(U->begin() + rand() % U->size(), RandCh());
-    } else { // At MaxLen.
-      uint8_t Ch = RandCh();
-      size_t Idx = rand() % U->size();
-      (*U)[Idx] = Ch;
+    if (Size < MaxSize) {
+      // Insert new value at Data[Idx].
+      memmove(Data + Idx + 1, Data + Idx, Size - Idx);
+      Data[Idx] = RandCh();
     }
+    Data[Idx] = RandCh();
     break;
-  default:
-    {
-      size_t Idx = rand() % U->size();
-      (*U)[Idx] = FlipRandomBit((*U)[Idx]);
-    }
+  case 2:
+    Data[Idx] = FlipRandomBit(Data[Idx]);
     break;
   }
-  assert(!U->empty());
+  assert(Size > 0);
+  return Size;
 }
 
 }  // namespace fuzzer
index fc66388..a9acec1 100644 (file)
@@ -21,6 +21,10 @@ set(Tests
   ${DFSanTests}
   )
 
+set(CustomMainTests
+  UserSuppliedFuzzerTest
+  )
+
 
 set(TestBinaries)
 
@@ -34,6 +38,17 @@ foreach(Test ${Tests})
   set(TestBinaries ${TestBinaries} LLVMFuzzer-${Test})
 endforeach()
 
+foreach(Test ${CustomMainTests})
+  add_executable(LLVMFuzzer-${Test}
+    ${Test}.cpp
+    )
+  target_link_libraries(LLVMFuzzer-${Test}
+    LLVMFuzzerNoMain
+    )
+  set(TestBinaries ${TestBinaries} LLVMFuzzer-${Test})
+endforeach()
+
+
 configure_lit_site_cfg(
   ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
   ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
@@ -49,7 +64,7 @@ include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include)
 
 add_executable(LLVMFuzzer-Unittest
   FuzzerUnittest.cpp
-  $<TARGET_OBJECTS:LLVMFuzzerNoMain>
+  $<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
   )
 
 target_link_libraries(LLVMFuzzer-Unittest
index 7b429b7..50f2f99 100644 (file)
@@ -51,7 +51,10 @@ TEST(Fuzzer, CrossOver) {
   for (size_t Len = 1; Len < 8; Len++) {
     std::set<Unit> FoundUnits, ExpectedUnitsWitThisLength;
     for (int Iter = 0; Iter < 3000; Iter++) {
-      CrossOver(A, B, &C, Len);
+      C.resize(Len);
+      size_t NewSize = CrossOver(A.data(), A.size(), B.data(), B.size(),
+                                 C.data(), C.size());
+      C.resize(NewSize);
       FoundUnits.insert(C);
     }
     for (const Unit &U : Expected)
diff --git a/lib/Fuzzer/test/UserSuppliedFuzzerTest.cpp b/lib/Fuzzer/test/UserSuppliedFuzzerTest.cpp
new file mode 100644 (file)
index 0000000..b46313d
--- /dev/null
@@ -0,0 +1,47 @@
+// Simple test for a fuzzer.
+// The fuzzer must find the string "Hi!" preceded by a magic value.
+// Uses UserSuppliedFuzzer which ensures that the magic is present.
+#include <cstdint>
+#include <cassert>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
+
+#include "FuzzerInterface.h"
+
+static const uint64_t kMagic = 8860221463604ULL;
+
+class MyFuzzer : public fuzzer::UserSuppliedFuzzer {
+ public:
+  void TargetFunction(const uint8_t *Data, size_t Size) {
+    if (Size <= 10) return;
+    if (memcmp(Data, &kMagic, sizeof(kMagic))) return;
+    // It's hard to get here w/o advanced fuzzing techniques (e.g. cmp tracing).
+    // So, we simply 'fix' the data in the custom mutator.
+    if (Data[8] == 'H') {
+      if (Data[9] == 'i') {
+        if (Data[10] == '!') {
+          std::cout << "BINGO; Found the target, exiting\n";
+          exit(1);
+        }
+      }
+    }
+  }
+  // Custom mutator.
+  virtual size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+    assert(MaxSize > sizeof(kMagic));
+    if (Size < sizeof(kMagic))
+      Size = sizeof(kMagic);
+    // "Fix" the data, then mutate.
+    memcpy(Data, &kMagic, std::min(MaxSize, sizeof(kMagic)));
+    return BasicMutate(Data + sizeof(kMagic), Size - sizeof(kMagic),
+                       MaxSize - sizeof(kMagic));
+  }
+  // No need to redefine CrossOver() here.
+};
+
+int main(int argc, char **argv) {
+  MyFuzzer F;
+  fuzzer::FuzzerDriver(argc, argv, F);
+}
index aa2c794..f27be80 100644 (file)
@@ -26,3 +26,5 @@ RUN: not ./LLVMFuzzer-DFSanMemcmpTest-DFSan -use_traces=1 -seed=1 -runs=100 -tim
 
 RUN: not ./LLVMFuzzer-CxxTokensTest -seed=1 -timeout=15 -tokens=%S/../cxx_fuzzer_tokens.txt 2>&1 | FileCheck %s
 
+RUN: not ./LLVMFuzzer-UserSuppliedFuzzerTest -seed=1 -timeout=15 2>&1 | FileCheck %s
+