Bug 1321248 - Add Fuzzer registry support for custom mutators r=franziskus
authorTim Taubert <ttaubert@mozilla.com>
Thu, 01 Dec 2016 13:51:51 +0100
changeset 12937 751e9db20c0416ccc80cee69d612a4ff43ea2ed8
parent 12936 b951e520167a37759a13b62744e34ab6287916ea
child 12938 5796201e791e6cbffc3615cb0c894cf1b0fc09a1
push id1848
push userttaubert@mozilla.com
push dateThu, 01 Dec 2016 12:53:19 +0000
reviewersfranziskus
bugs1321248
Bug 1321248 - Add Fuzzer registry support for custom mutators r=franziskus Differential Revision: https://nss-review.dev.mozaws.net/D109
fuzz/asn1_mutators.cc
fuzz/asn1_mutators.h
fuzz/clone_corpus.sh
fuzz/clone_libfuzzer.sh
fuzz/fuzz.gyp
fuzz/nssfuzz.cc
fuzz/pkcs8_target.cc
fuzz/quickder_targets.cc
fuzz/registry.h
new file mode 100644
--- /dev/null
+++ b/fuzz/asn1_mutators.cc
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <assert.h>
+#include <string.h>
+#include <tuple>
+
+#include "FuzzerRandom.h"
+#include "asn1_mutators.h"
+
+using namespace std;
+
+static tuple<uint8_t *, size_t> ParseItem(uint8_t *Data, size_t MaxLength) {
+  // Short form. Bit 8 has value "0" and bits 7-1 give the length.
+  if ((Data[1] & 0x80) == 0) {
+    size_t length = min(static_cast<size_t>(Data[1]), MaxLength - 2);
+    return make_tuple(&Data[2], length);
+  }
+
+  // Constructed, indefinite length. Read until {0x00, 0x00}.
+  if (Data[1] == 0x80) {
+    void *offset = memmem(&Data[2], MaxLength - 2, "\0", 2);
+    size_t length = offset ? (static_cast<uint8_t *>(offset) - &Data[2]) + 2
+                           : MaxLength - 2;
+    return make_tuple(&Data[2], length);
+  }
+
+  // Long form. Two to 127 octets. Bit 8 of first octet has value "1"
+  // and bits 7-1 give the number of additional length octets.
+  size_t octets = min(static_cast<size_t>(Data[1] & 0x7f), MaxLength - 2);
+
+  // Handle lengths bigger than 32 bits.
+  if (octets > 4) {
+    // Ignore any further children, assign remaining length.
+    return make_tuple(&Data[2] + octets, MaxLength - 2 - octets);
+  }
+
+  // Parse the length.
+  size_t length = 0;
+  for (size_t j = 0; j < octets; j++) {
+    length = (length << 8) | Data[2 + j];
+  }
+
+  length = min(length, MaxLength - 2 - octets);
+  return make_tuple(&Data[2] + octets, length);
+}
+
+static vector<uint8_t *> ParseItems(uint8_t *Data, size_t Size) {
+  vector<uint8_t *> items;
+  vector<size_t> lengths;
+
+  // The first item is always the whole corpus.
+  items.push_back(Data);
+  lengths.push_back(Size);
+
+  // Can't use iterators here because the `items` vector is modified inside the
+  // loop. That's safe as long as we always check `items.size()` before every
+  // iteration, and only call `.push_back()` to append new items we found.
+  // Items are accessed through `items.at()`, we hold no references.
+  for (size_t i = 0; i < items.size(); i++) {
+    uint8_t *item = items.at(i);
+    size_t remaining = lengths.at(i);
+
+    // Empty or primitive items have no children.
+    if (remaining == 0 || (0x20 & item[0]) == 0) {
+      continue;
+    }
+
+    while (remaining > 2) {
+      uint8_t *content;
+      size_t length;
+
+      tie(content, length) = ParseItem(item, remaining);
+
+      if (length > 0) {
+        // Record the item.
+        items.push_back(content);
+
+        // Record the length for further parsing.
+        lengths.push_back(length);
+      }
+
+      // Reduce number of bytes left in current item.
+      remaining -= length + (content - item);
+
+      // Skip the item we just parsed.
+      item = content + length;
+    }
+  }
+
+  return items;
+}
+
+size_t ASN1MutatorFlipConstructed(uint8_t *Data, size_t Size, size_t MaxSize,
+                                  unsigned int Seed) {
+  fuzzer::Random R(Seed);
+  auto items = ParseItems(Data, Size);
+  uint8_t *item = items.at(R(items.size()));
+
+  // Flip "constructed" type bit.
+  item[0] ^= 0x20;
+
+  return Size;
+}
+
+size_t ASN1MutatorChangeType(uint8_t *Data, size_t Size, size_t MaxSize,
+                             unsigned int Seed) {
+  fuzzer::Random R(Seed);
+  auto items = ParseItems(Data, Size);
+  uint8_t *item = items.at(R(items.size()));
+
+  // Change type to a random int [0, 31).
+  item[0] = R(31);
+
+  return Size;
+}
new file mode 100644
--- /dev/null
+++ b/fuzz/asn1_mutators.h
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef asn1_mutators_h__
+#define asn1_mutators_h__
+
+#include <stdint.h>
+#include <cstddef>
+
+size_t ASN1MutatorFlipConstructed(uint8_t *Data, size_t Size, size_t MaxSize,
+                                  unsigned int Seed);
+size_t ASN1MutatorChangeType(uint8_t *Data, size_t Size, size_t MaxSize,
+                             unsigned int Seed);
+
+#endif  // asn1_mutators_h__
--- a/fuzz/clone_corpus.sh
+++ b/fuzz/clone_corpus.sh
@@ -1,4 +1,4 @@
 #!/bin/sh
 
 d=$(dirname $0)
-exec $d/git-copy.sh https://github.com/mozilla/nss-fuzzing-corpus master $d/corpus
+$d/git-copy.sh https://github.com/mozilla/nss-fuzzing-corpus master $d/corpus
--- a/fuzz/clone_libfuzzer.sh
+++ b/fuzz/clone_libfuzzer.sh
@@ -1,4 +1,22 @@
 #!/bin/sh
 
 d=$(dirname $0)
-exec $d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer 1b543d6e5073b56be214394890c9193979a3d7e1 $d/libFuzzer
+$d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer 1b543d6e5073b56be214394890c9193979a3d7e1 $d/libFuzzer
+
+cat <<EOF | patch -p0 -d $d
+diff --git libFuzzer/FuzzerMutate.cpp libFuzzer/FuzzerMutate.cpp
+--- libFuzzer/FuzzerMutate.cpp
++++ libFuzzer/FuzzerMutate.cpp
+@@ -53,10 +53,9 @@
+     DefaultMutators.push_back(
+         {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
+
++  Mutators = DefaultMutators;
+   if (EF->LLVMFuzzerCustomMutator)
+     Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
+-  else
+-    Mutators = DefaultMutators;
+
+   if (EF->LLVMFuzzerCustomCrossOver)
+     Mutators.push_back(
+EOF
--- a/fuzz/fuzz.gyp
+++ b/fuzz/fuzz.gyp
@@ -20,46 +20,47 @@
         'libFuzzer/FuzzerMutate.cpp',
         'libFuzzer/FuzzerSHA1.cpp',
         'libFuzzer/FuzzerTracePC.cpp',
         'libFuzzer/FuzzerTraceState.cpp',
         'libFuzzer/FuzzerUtil.cpp',
         'libFuzzer/FuzzerUtilDarwin.cpp',
         'libFuzzer/FuzzerUtilLinux.cpp',
       ],
-      'cflags': [
-        '-O2',
-      ],
-      'cflags/': [
-        ['exclude', '-fsanitize='],
-        ['exclude', '-fsanitize-'],
-      ],
-      'xcode_settings': {
-        'GCC_OPTIMIZATION_LEVEL': '2', # -O2
-        'OTHER_CFLAGS/': [
-          ['exclude', '-fsanitize='],
-          ['exclude', '-fsanitize-'],
-        ],
-      },
     },
     {
       'target_name': 'nssfuzz',
       'type': 'executable',
       'sources': [
+        'asn1_mutators.cc',
         'nssfuzz.cc',
         'pkcs8_target.cc',
         'quickder_targets.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         'libFuzzer',
       ]
     }
   ],
   'target_defaults': {
     'include_dirs': [
       'libFuzzer',
     ],
+    'cflags': [
+      '-O2',
+    ],
+    'cflags/': [
+      ['exclude', '-fsanitize='],
+      ['exclude', '-fsanitize-'],
+    ],
+    'xcode_settings': {
+      'GCC_OPTIMIZATION_LEVEL': '2', # -O2
+      'OTHER_CFLAGS/': [
+        ['exclude', '-fsanitize='],
+        ['exclude', '-fsanitize-'],
+      ],
+    },
   },
   'variables': {
     'module': 'nss',
   }
 }
--- a/fuzz/nssfuzz.cc
+++ b/fuzz/nssfuzz.cc
@@ -1,27 +1,27 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <iomanip>
 #include <iostream>
-#include <memory>
-
-#include "keyhi.h"
-#include "pk11pub.h"
 
 #include "FuzzerInternal.h"
+#include "FuzzerMutate.h"
+#include "FuzzerRandom.h"
 #include "registry.h"
 #include "shared.h"
 
 using namespace std;
 
+static vector<Mutator> gMutators;
+
 class Args {
  public:
   Args(int argc, char **argv) : args_(argv, argv + argc) {}
 
   string &operator[](const int idx) { return args_[idx]; }
 
   bool Has(const string &arg) {
     return any_of(args_.begin(), args_.end(),
@@ -122,16 +122,20 @@ int main(int argc, char **argv) {
 
   if (args.count() < 2 || !Registry::Has(args[1])) {
     printUsage(args);
     return 1;
   }
 
   string targetName(args[1]);
 
+  // Add target mutators.
+  auto mutators = Registry::Mutators(targetName);
+  gMutators.insert(gMutators.end(), mutators.begin(), mutators.end());
+
   // Remove the target argument when -workers=x or -jobs=y is NOT given.
   // If both are given, libFuzzer will spawn multiple processes for the target.
   if (!args.Has("-workers=") || !args.Has("-jobs=")) {
     args.Remove(1);
   }
 
   // Set default max_len arg, if none given and we're not merging.
   if (!args.Has("-max_len=") && !args.Has("-merge=1")) {
@@ -141,8 +145,19 @@ int main(int argc, char **argv) {
 
   // Hand control to the libFuzzer driver.
   vector<char *> args_new(args.argv());
   argc = args_new.size();
   argv = args_new.data();
 
   return fuzzer::FuzzerDriver(&argc, &argv, Registry::Func(targetName));
 }
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+                                          size_t MaxSize, unsigned int Seed) {
+  if (gMutators.empty()) {
+    return 0;
+  }
+
+  // Forward to a pseudorandom mutator.
+  fuzzer::Random R(Seed);
+  return gMutators.at(R(gMutators.size()))(Data, Size, MaxSize, Seed);
+}
--- a/fuzz/pkcs8_target.cc
+++ b/fuzz/pkcs8_target.cc
@@ -29,9 +29,10 @@ extern "C" int pkcs8_fuzzing_target(cons
                                                nullptr) == SECSuccess) {
     SECKEY_DestroyPrivateKey(key);
   }
 
   PK11_FreeSlot(slot);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("pkcs8", pkcs8_fuzzing_target, 2048, "PKCS#8 Import")
+REGISTER_FUZZING_TARGET("pkcs8", pkcs8_fuzzing_target, 2048, "PKCS#8 Import",
+                        {})
--- a/fuzz/quickder_targets.cc
+++ b/fuzz/quickder_targets.cc
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdint.h>
 
+#include "asn1_mutators.h"
 #include "cert.h"
-
 #include "registry.h"
 
 void QuickDERDecode(void *dst, const SEC_ASN1Template *tpl, const uint8_t *buf,
                     size_t len) {
   PORTCheapArenaPool pool;
   SECItem data = {siBuffer, const_cast<unsigned char *>(buf),
                   static_cast<unsigned int>(len)};
 
@@ -20,17 +20,19 @@ void QuickDERDecode(void *dst, const SEC
 }
 
 extern "C" int cert_fuzzing_target(const uint8_t *Data, size_t Size) {
   CERTCertificate cert;
   QuickDERDecode(&cert, SEC_SignedCertificateTemplate, Data, Size);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("cert", cert_fuzzing_target, 3072, "Certificate Import")
+REGISTER_FUZZING_TARGET("cert", cert_fuzzing_target, 3072, "Certificate Import",
+                        {&ASN1MutatorFlipConstructed, &ASN1MutatorChangeType})
 
 extern "C" int spki_fuzzing_target(const uint8_t *Data, size_t Size) {
   CERTSubjectPublicKeyInfo spki;
   QuickDERDecode(&spki, CERT_SubjectPublicKeyInfoTemplate, Data, Size);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("spki", spki_fuzzing_target, 1024, "SPKI Import")
+REGISTER_FUZZING_TARGET("spki", spki_fuzzing_target, 1024, "SPKI Import",
+                        {&ASN1MutatorFlipConstructed, &ASN1MutatorChangeType})
--- a/fuzz/registry.h
+++ b/fuzz/registry.h
@@ -6,66 +6,74 @@
 
 #ifndef registry_h__
 #define registry_h__
 
 #include <map>
 #include "FuzzerInternal.h"
 #include "nss.h"
 
+using namespace fuzzer;
+using namespace std;
+
+typedef decltype(LLVMFuzzerCustomMutator)* Mutator;
+
 class Registry {
  public:
-  static void Add(std::string name, fuzzer::UserCallback func, uint16_t max_len,
-                  std::string desc) {
+  static void Add(string name, UserCallback func, uint16_t max_len, string desc,
+                  vector<Mutator> mutators = {}) {
     assert(!Has(name));
-    GetInstance().targets_[name] = TargetData(func, max_len, desc);
+    GetInstance().targets_[name] = TargetData(func, max_len, desc, mutators);
   }
 
-  static bool Has(std::string name) {
+  static bool Has(string name) {
     return GetInstance().targets_.count(name) > 0;
   }
 
-  static fuzzer::UserCallback Func(std::string name) {
+  static UserCallback Func(string name) {
     assert(Has(name));
-    return std::get<0>(Get(name));
+    return get<0>(Get(name));
+  }
+
+  static uint16_t MaxLen(string name) {
+    assert(Has(name));
+    return get<1>(Get(name));
   }
 
-  static uint16_t MaxLen(std::string name) {
+  static string& Desc(string name) {
     assert(Has(name));
-    return std::get<1>(Get(name));
+    return get<2>(Get(name));
   }
 
-  static std::string& Desc(std::string name) {
+  static vector<Mutator>& Mutators(string name) {
     assert(Has(name));
-    return std::get<2>(Get(name));
+    return get<3>(Get(name));
   }
 
-  static std::vector<std::string> Names() {
-    std::vector<std::string> names;
+  static vector<string> Names() {
+    vector<string> names;
     for (auto& it : GetInstance().targets_) {
       names.push_back(it.first);
     }
     return names;
   }
 
  private:
-  typedef std::tuple<fuzzer::UserCallback, uint16_t, std::string> TargetData;
+  typedef tuple<UserCallback, uint16_t, string, vector<Mutator>> TargetData;
 
   static Registry& GetInstance() {
     static Registry registry;
     return registry;
   }
 
-  static TargetData& Get(std::string name) {
-    return GetInstance().targets_[name];
-  }
+  static TargetData& Get(string name) { return GetInstance().targets_[name]; }
 
   Registry() {}
 
-  std::map<std::string, TargetData> targets_;
+  map<string, TargetData> targets_;
 };
 
-#define REGISTER_FUZZING_TARGET(name, func, max_len, desc)     \
-  static void __attribute__((constructor)) Register_##func() { \
-    Registry::Add(name, func, max_len, desc);                  \
+#define REGISTER_FUZZING_TARGET(name, func, max_len, desc, ...) \
+  static void __attribute__((constructor)) Register_##func() {  \
+    Registry::Add(name, func, max_len, desc, __VA_ARGS__);      \
   }
 
 #endif  // registry_h__