summaryrefslogtreecommitdiff
path: root/fuzz
diff options
context:
space:
mode:
authorTim Taubert <ttaubert@mozilla.com>2016-12-01 13:51:51 +0100
committerTim Taubert <ttaubert@mozilla.com>2016-12-01 13:51:51 +0100
commit1536ed7c4a1b5ba568243281097172982c557840 (patch)
tree678f88abfd9231e59b8d64a3cc1fd8f31af5232a /fuzz
parent56cfb09269745c7eb3b827e8723a8446dd65573b (diff)
downloadnss-hg-1536ed7c4a1b5ba568243281097172982c557840.tar.gz
Bug 1321248 - Add Fuzzer registry support for custom mutators r=franziskus
Differential Revision: https://nss-review.dev.mozaws.net/D109
Diffstat (limited to 'fuzz')
-rw-r--r--fuzz/asn1_mutators.cc117
-rw-r--r--fuzz/asn1_mutators.h16
-rwxr-xr-xfuzz/clone_corpus.sh2
-rwxr-xr-xfuzz/clone_libfuzzer.sh20
-rw-r--r--fuzz/fuzz.gyp29
-rw-r--r--fuzz/nssfuzz.cc23
-rw-r--r--fuzz/pkcs8_target.cc3
-rw-r--r--fuzz/quickder_targets.cc8
-rw-r--r--fuzz/registry.h48
9 files changed, 222 insertions, 44 deletions
diff --git a/fuzz/asn1_mutators.cc b/fuzz/asn1_mutators.cc
new file mode 100644
index 000000000..a7c952290
--- /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;
+}
diff --git a/fuzz/asn1_mutators.h b/fuzz/asn1_mutators.h
new file mode 100644
index 000000000..8bf02d49f
--- /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__
diff --git a/fuzz/clone_corpus.sh b/fuzz/clone_corpus.sh
index a41cbc0c5..9c17d2062 100755
--- 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
diff --git a/fuzz/clone_libfuzzer.sh b/fuzz/clone_libfuzzer.sh
index 91c93de31..b2dbc5e17 100755
--- 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
diff --git a/fuzz/fuzz.gyp b/fuzz/fuzz.gyp
index 4321c5cf5..3d25d005d 100644
--- a/fuzz/fuzz.gyp
+++ b/fuzz/fuzz.gyp
@@ -25,25 +25,12 @@
'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',
@@ -58,6 +45,20 @@
'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',
diff --git a/fuzz/nssfuzz.cc b/fuzz/nssfuzz.cc
index d9769309a..5d76edabe 100644
--- a/fuzz/nssfuzz.cc
+++ b/fuzz/nssfuzz.cc
@@ -6,17 +6,17 @@
#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) {}
@@ -127,6 +127,10 @@ int main(int argc, char **argv) {
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=")) {
@@ -146,3 +150,14 @@ int main(int argc, char **argv) {
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);
+}
diff --git a/fuzz/pkcs8_target.cc b/fuzz/pkcs8_target.cc
index 8b6ed7b57..4ace36adc 100644
--- a/fuzz/pkcs8_target.cc
+++ b/fuzz/pkcs8_target.cc
@@ -34,4 +34,5 @@ extern "C" int pkcs8_fuzzing_target(const uint8_t *Data, size_t Size) {
return 0;
}
-REGISTER_FUZZING_TARGET("pkcs8", pkcs8_fuzzing_target, 2048, "PKCS#8 Import")
+REGISTER_FUZZING_TARGET("pkcs8", pkcs8_fuzzing_target, 2048, "PKCS#8 Import",
+ {})
diff --git a/fuzz/quickder_targets.cc b/fuzz/quickder_targets.cc
index 251772196..84bda7fb1 100644
--- a/fuzz/quickder_targets.cc
+++ b/fuzz/quickder_targets.cc
@@ -4,8 +4,8 @@
#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,
@@ -25,7 +25,8 @@ extern "C" int cert_fuzzing_target(const uint8_t *Data, size_t 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;
@@ -33,4 +34,5 @@ extern "C" int spki_fuzzing_target(const uint8_t *Data, size_t 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})
diff --git a/fuzz/registry.h b/fuzz/registry.h
index 760118dec..902f3b29a 100644
--- a/fuzz/registry.h
+++ b/fuzz/registry.h
@@ -11,35 +11,45 @@
#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 get<0>(Get(name));
+ }
+
+ static uint16_t MaxLen(string name) {
assert(Has(name));
- return std::get<0>(Get(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);
}
@@ -47,25 +57,23 @@ class Registry {
}
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__