diff options
author | Tim Taubert <ttaubert@mozilla.com> | 2016-12-01 13:51:51 +0100 |
---|---|---|
committer | Tim Taubert <ttaubert@mozilla.com> | 2016-12-01 13:51:51 +0100 |
commit | 1536ed7c4a1b5ba568243281097172982c557840 (patch) | |
tree | 678f88abfd9231e59b8d64a3cc1fd8f31af5232a /fuzz | |
parent | 56cfb09269745c7eb3b827e8723a8446dd65573b (diff) | |
download | nss-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.cc | 117 | ||||
-rw-r--r-- | fuzz/asn1_mutators.h | 16 | ||||
-rwxr-xr-x | fuzz/clone_corpus.sh | 2 | ||||
-rwxr-xr-x | fuzz/clone_libfuzzer.sh | 20 | ||||
-rw-r--r-- | fuzz/fuzz.gyp | 29 | ||||
-rw-r--r-- | fuzz/nssfuzz.cc | 23 | ||||
-rw-r--r-- | fuzz/pkcs8_target.cc | 3 | ||||
-rw-r--r-- | fuzz/quickder_targets.cc | 8 | ||||
-rw-r--r-- | fuzz/registry.h | 48 |
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__ |