Skip to content

Commit

Permalink
Bug 1321248 - Add Fuzzer registry support for custom mutators r=franz…
Browse files Browse the repository at this point in the history
…iskus

Differential Revision: https://nss-review.dev.mozaws.net/D109
  • Loading branch information
Tim Taubert committed Dec 1, 2016
1 parent 509111a commit 516f4a6
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 44 deletions.
117 changes: 117 additions & 0 deletions 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;
}
16 changes: 16 additions & 0 deletions 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__
2 changes: 1 addition & 1 deletion 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
20 changes: 19 additions & 1 deletion 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
29 changes: 15 additions & 14 deletions fuzz/fuzz.gyp
Expand Up @@ -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',
Expand All @@ -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',
Expand Down
23 changes: 19 additions & 4 deletions fuzz/nssfuzz.cc
Expand Up @@ -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) {}
Expand Down Expand Up @@ -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=")) {
Expand All @@ -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);
}
3 changes: 2 additions & 1 deletion fuzz/pkcs8_target.cc
Expand Up @@ -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",
{})
8 changes: 5 additions & 3 deletions fuzz/quickder_targets.cc
Expand Up @@ -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,
Expand All @@ -25,12 +25,14 @@ 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;
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})
48 changes: 28 additions & 20 deletions fuzz/registry.h
Expand Up @@ -11,61 +11,69 @@
#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);
}
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__

0 comments on commit 516f4a6

Please sign in to comment.