summaryrefslogtreecommitdiff
path: root/fuzz
diff options
context:
space:
mode:
authorTim Taubert <ttaubert@mozilla.com>2017-03-24 11:18:52 +0100
committerTim Taubert <ttaubert@mozilla.com>2017-03-24 11:18:52 +0100
commit14dce5c17c2b2993ffd4fa456524b781f46a2dc6 (patch)
tree31724255437e0c38c5aba50459f21ea0d797129f /fuzz
parent81311a26f78c579d457a1d0e04f7e7e382e93235 (diff)
downloadnss-hg-14dce5c17c2b2993ffd4fa456524b781f46a2dc6.tar.gz
Bug 1348775 - Add custom TLS mutators for libFuzzer fuzzing targets r=franziskus
Differential Revision: https://nss-review.dev.mozaws.net/D261
Diffstat (limited to 'fuzz')
-rw-r--r--fuzz/fuzz.gyp56
-rw-r--r--fuzz/pkcs8_target.cc10
-rw-r--r--fuzz/quickder_target.cc10
-rw-r--r--fuzz/shared.cc18
-rw-r--r--fuzz/shared.h14
-rw-r--r--fuzz/tls_client_target.cc18
-rw-r--r--fuzz/tls_mutators.cc276
-rw-r--r--fuzz/tls_mutators.h23
-rw-r--r--fuzz/tls_server_target.cc18
9 files changed, 390 insertions, 53 deletions
diff --git a/fuzz/fuzz.gyp b/fuzz/fuzz.gyp
index 180f08465..da52d6e37 100644
--- a/fuzz/fuzz.gyp
+++ b/fuzz/fuzz.gyp
@@ -25,7 +25,12 @@
'targets': [
{
'target_name': 'fuzz_base',
+ 'type': 'static_library',
+ 'sources': [
+ 'shared.cc',
+ ],
'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/lib/certdb/certdb.gyp:certdb',
'<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
'<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
@@ -41,7 +46,6 @@
],
'conditions': [
['fuzz_oss==0', {
- 'type': 'static_library',
'sources': [
'<!@(ls <(DEPTH)/fuzz/libFuzzer/*.cpp)',
],
@@ -54,7 +58,6 @@
],
},
}, {
- 'type': 'none',
'all_dependent_settings': {
'libraries': ['-lFuzzingEngine'],
}
@@ -248,12 +251,11 @@
],
},
{
- 'target_name': 'nssfuzz-tls-client',
- 'type': 'executable',
+ 'target_name': 'nssfuzz-tls-base',
+ 'type': 'static_library',
'sources': [
- 'tls_client_config.cc',
- 'tls_client_target.cc',
'tls_common.cc',
+ 'tls_mutators.cc',
'tls_socket.cc',
],
'dependencies': [
@@ -262,40 +264,40 @@
'fuzz_base',
],
'include_dirs': [
- '<(DEPTH)/lib/freebl',
+ '<(DEPTH)/lib/ssl',
],
- 'conditions': [
- [ 'fuzz_tls==1', {
- 'defines': [
- 'UNSAFE_FUZZER_MODE',
- ],
- }],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(DEPTH)/lib/freebl',
+ '<(DEPTH)/lib/ssl',
+ ],
+ },
+ },
+ {
+ 'target_name': 'nssfuzz-tls-client',
+ 'type': 'executable',
+ 'sources': [
+ 'tls_client_config.cc',
+ 'tls_client_target.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports',
+ '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
+ 'nssfuzz-tls-base',
],
},
{
'target_name': 'nssfuzz-tls-server',
'type': 'executable',
'sources': [
- 'tls_common.cc',
- 'tls_socket.cc',
'tls_server_certs.cc',
'tls_server_config.cc',
'tls_server_target.cc',
],
'dependencies': [
- '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
'<(DEPTH)/exports.gyp:nss_exports',
- 'fuzz_base',
- ],
- 'include_dirs': [
- '<(DEPTH)/lib/freebl',
- ],
- 'conditions': [
- [ 'fuzz_tls==1', {
- 'defines': [
- 'UNSAFE_FUZZER_MODE',
- ],
- }],
+ '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
+ 'nssfuzz-tls-base',
],
},
{
diff --git a/fuzz/pkcs8_target.cc b/fuzz/pkcs8_target.cc
index b6b90c99c..6ce6f6d04 100644
--- a/fuzz/pkcs8_target.cc
+++ b/fuzz/pkcs8_target.cc
@@ -31,9 +31,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
return 0;
}
-extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
- size_t MaxSize, unsigned int Seed) {
- static Mutators mutators = {&ASN1MutatorFlipConstructed,
- &ASN1MutatorChangeType};
- return CustomMutate(mutators, Data, Size, MaxSize, Seed);
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size,
+ size_t max_size, unsigned int seed) {
+ return CustomMutate(
+ Mutators({ASN1MutatorFlipConstructed, ASN1MutatorChangeType}), data, size,
+ max_size, seed);
}
diff --git a/fuzz/quickder_target.cc b/fuzz/quickder_target.cc
index 4d6277d3b..e24627590 100644
--- a/fuzz/quickder_target.cc
+++ b/fuzz/quickder_target.cc
@@ -77,9 +77,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
return 0;
}
-extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
- size_t MaxSize, unsigned int Seed) {
- static Mutators mutators = {&ASN1MutatorFlipConstructed,
- &ASN1MutatorChangeType};
- return CustomMutate(mutators, Data, Size, MaxSize, Seed);
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size,
+ size_t max_size, unsigned int seed) {
+ return CustomMutate(
+ Mutators({ASN1MutatorFlipConstructed, ASN1MutatorChangeType}), data, size,
+ max_size, seed);
}
diff --git a/fuzz/shared.cc b/fuzz/shared.cc
new file mode 100644
index 000000000..47fb21638
--- /dev/null
+++ b/fuzz/shared.cc
@@ -0,0 +1,18 @@
+/* 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 "shared.h"
+
+size_t CustomMutate(Mutators mutators, uint8_t *data, size_t size,
+ size_t max_size, unsigned int seed) {
+ std::mt19937 rng(seed);
+ static std::bernoulli_distribution bdist;
+
+ if (bdist(rng)) {
+ std::uniform_int_distribution<size_t> idist(0, mutators.size() - 1);
+ return mutators.at(idist(rng))(data, size, max_size, seed);
+ }
+
+ return LLVMFuzzerMutate(data, size, max_size);
+}
diff --git a/fuzz/shared.h b/fuzz/shared.h
index 24ca51f85..35621eb9d 100644
--- a/fuzz/shared.h
+++ b/fuzz/shared.h
@@ -24,17 +24,7 @@ class NSSDatabase {
typedef std::vector<decltype(LLVMFuzzerCustomMutator) *> Mutators;
-size_t CustomMutate(Mutators &mutators, uint8_t *Data, size_t Size,
- size_t MaxSize, unsigned int Seed) {
- std::mt19937 rng(Seed);
- static std::bernoulli_distribution bdist;
-
- if (bdist(rng)) {
- std::uniform_int_distribution<size_t> idist(0, mutators.size() - 1);
- return mutators.at(idist(rng))(Data, Size, MaxSize, Seed);
- }
-
- return LLVMFuzzerMutate(Data, Size, MaxSize);
-}
+size_t CustomMutate(Mutators mutators, uint8_t *data, size_t size,
+ size_t max_size, unsigned int seed);
#endif // shared_h__
diff --git a/fuzz/tls_client_target.cc b/fuzz/tls_client_target.cc
index 4b40257a9..82e4a5dfb 100644
--- a/fuzz/tls_client_target.cc
+++ b/fuzz/tls_client_target.cc
@@ -13,6 +13,7 @@
#include "shared.h"
#include "tls_client_config.h"
#include "tls_common.h"
+#include "tls_mutators.h"
#include "tls_socket.h"
static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checksig,
@@ -79,10 +80,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) {
// Clear the cache. We never want to resume as we couldn't reproduce that.
SSL_ClearSessionCache();
-#ifdef UNSAFE_FUZZER_MODE
// Reset the RNG state.
assert(RNG_RandomUpdate(NULL, 0) == SECSuccess);
-#endif
// Create and import dummy socket.
std::unique_ptr<DummyPrSocket> socket(new DummyPrSocket(data, len));
@@ -101,3 +100,18 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) {
return 0;
}
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
+ size_t max_size, unsigned int seed) {
+ return CustomMutate({TlsMutatorDropRecord, TlsMutatorShuffleRecords,
+ TlsMutatorDuplicateRecord, TlsMutatorTruncateRecord,
+ TlsMutatorFragmentRecord},
+ data, size, max_size, seed);
+}
+
+extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t* data1, size_t size1,
+ const uint8_t* data2, size_t size2,
+ uint8_t* out, size_t max_out_size,
+ unsigned int seed) {
+ return TlsCrossOver(data1, size1, data2, size2, out, max_out_size, seed);
+}
diff --git a/fuzz/tls_mutators.cc b/fuzz/tls_mutators.cc
new file mode 100644
index 000000000..ccf1935bf
--- /dev/null
+++ b/fuzz/tls_mutators.cc
@@ -0,0 +1,276 @@
+/* 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 "shared.h"
+#include "tls_parser.h"
+
+#include "ssl.h"
+#include "sslimpl.h"
+
+using namespace nss_test;
+
+// Helper class to simplify TLS record manipulation.
+class Record {
+ public:
+ static std::unique_ptr<Record> Create(const uint8_t *data, size_t size,
+ size_t remaining) {
+ return std::unique_ptr<Record>(new Record(data, size, remaining));
+ }
+
+ void insert_before(const std::unique_ptr<Record> &other) {
+ assert(data_ && size_ > 0);
+
+ // Copy data in case other == this.
+ uint8_t buf[size_];
+ memcpy(buf, data_, size_);
+
+ uint8_t *dest = const_cast<uint8_t *>(other->data());
+ // Make room for the record we want to insert.
+ memmove(dest + size_, other->data(), other->size() + other->remaining());
+ // Insert the record.
+ memcpy(dest, buf, size_);
+ }
+
+ void truncate(size_t length) {
+ assert(length >= 5);
+ uint8_t *dest = const_cast<uint8_t *>(data_);
+ (void)ssl_EncodeUintX(length - 5, 2, &dest[3]);
+ memmove(dest + length, data_ + size_, remaining_);
+ }
+
+ void drop() {
+ uint8_t *dest = const_cast<uint8_t *>(data_);
+ memmove(dest, data_ + size_, remaining_);
+ }
+
+ const uint8_t *data() { return data_; }
+ size_t remaining() { return remaining_; }
+ size_t size() { return size_; }
+
+ private:
+ Record(const uint8_t *data, size_t size, size_t remaining)
+ : data_(data), remaining_(remaining), size_(size) {}
+
+ const uint8_t *data_;
+ size_t remaining_;
+ size_t size_;
+};
+
+// Parse records contained in a given TLS transcript.
+std::vector<std::unique_ptr<Record>> ParseRecords(const uint8_t *data,
+ size_t size) {
+ std::vector<std::unique_ptr<Record>> records;
+ TlsParser parser(data, size);
+
+ while (parser.remaining()) {
+ size_t offset = parser.consumed();
+
+ uint32_t type;
+ if (!parser.Read(&type, 1)) {
+ break;
+ }
+
+ uint32_t version;
+ if (!parser.Read(&version, 2)) {
+ break;
+ }
+
+ DataBuffer fragment;
+ if (!parser.ReadVariable(&fragment, 2)) {
+ break;
+ }
+
+ records.push_back(
+ Record::Create(data + offset, fragment.len() + 5, parser.remaining()));
+ }
+
+ return records;
+}
+
+// Mutator that drops whole TLS records.
+size_t TlsMutatorDropRecord(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed) {
+ std::mt19937 rng(seed);
+
+ // Find TLS records in the corpus.
+ auto records = ParseRecords(data, size);
+ if (records.empty()) {
+ return 0;
+ }
+
+ // Pick a record to drop at random.
+ std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
+ auto &rec = records.at(dist(rng));
+
+ // Drop the record.
+ rec->drop();
+
+ // Return the new final size.
+ return size - rec->size();
+}
+
+// Mutator that shuffles TLS records in a transcript.
+size_t TlsMutatorShuffleRecords(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed) {
+ std::mt19937 rng(seed);
+
+ // Store the original corpus.
+ uint8_t buf[size];
+ memcpy(buf, data, size);
+
+ // Find TLS records in the corpus.
+ auto records = ParseRecords(buf, sizeof(buf));
+ if (records.empty()) {
+ return 0;
+ }
+
+ // Find offset of first record in target buffer.
+ uint8_t *dest = const_cast<uint8_t *>(ParseRecords(data, size).at(0)->data());
+
+ // Shuffle record order.
+ std::shuffle(records.begin(), records.end(), rng);
+
+ // Write records to their new positions.
+ for (auto &rec : records) {
+ memcpy(dest, rec->data(), rec->size());
+ dest += rec->size();
+ }
+
+ // Final size hasn't changed.
+ return size;
+}
+
+// Mutator that duplicates a single TLS record and randomly inserts it.
+size_t TlsMutatorDuplicateRecord(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed) {
+ std::mt19937 rng(seed);
+
+ // Find TLS records in the corpus.
+ const auto records = ParseRecords(data, size);
+ if (records.empty()) {
+ return 0;
+ }
+
+ // Pick a record to duplicate at random.
+ std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
+ auto &rec = records.at(dist(rng));
+ if (size + rec->size() > max_size) {
+ return 0;
+ }
+
+ // Insert before random record.
+ rec->insert_before(records.at(dist(rng)));
+
+ // Return the new final size.
+ return size + rec->size();
+}
+
+// Mutator that truncates a TLS record.
+size_t TlsMutatorTruncateRecord(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed) {
+ std::mt19937 rng(seed);
+
+ // Find TLS records in the corpus.
+ const auto records = ParseRecords(data, size);
+ if (records.empty()) {
+ return 0;
+ }
+
+ // Pick a record to truncate at random.
+ std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
+ auto &rec = records.at(dist(rng));
+
+ // Need a record with data.
+ if (rec->size() <= 5) {
+ return 0;
+ }
+
+ // Truncate.
+ std::uniform_int_distribution<size_t> dist2(5, rec->size() - 1);
+ size_t new_length = dist2(rng);
+ rec->truncate(new_length);
+
+ // Return the new final size.
+ return size + new_length - rec->size();
+}
+
+// Mutator that splits a TLS record in two.
+size_t TlsMutatorFragmentRecord(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed) {
+ std::mt19937 rng(seed);
+
+ if (size + 5 > max_size) {
+ return 0;
+ }
+
+ // Find TLS records in the corpus.
+ const auto records = ParseRecords(data, size);
+ if (records.empty()) {
+ return 0;
+ }
+
+ // Pick a record to fragment at random.
+ std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
+ auto &rec = records.at(dist(rng));
+ uint8_t *rdata = const_cast<uint8_t *>(rec->data());
+ size_t length = rec->size();
+ size_t content_length = length - 5;
+
+ if (content_length < 2) {
+ return 0;
+ }
+
+ // Assign a new length to the first fragment.
+ size_t new_length = content_length / 2;
+ uint8_t *content = ssl_EncodeUintX(new_length, 2, &rdata[3]);
+
+ // Make room for one more header.
+ memmove(content + new_length + 5, content + new_length,
+ rec->remaining() + content_length - new_length);
+
+ // Write second header.
+ memcpy(content + new_length, rdata, 3);
+ (void)ssl_EncodeUintX(content_length - new_length, 2,
+ &content[new_length + 3]);
+
+ return size + 5;
+}
+
+// Cross-over function that merges and shuffles two transcripts.
+size_t TlsCrossOver(const uint8_t *data1, size_t size1, const uint8_t *data2,
+ size_t size2, uint8_t *out, size_t max_out_size,
+ unsigned int seed) {
+ std::mt19937 rng(seed);
+
+ // Find TLS records in the corpus.
+ auto records1 = ParseRecords(data1, size1);
+ if (records1.empty()) {
+ return 0;
+ }
+
+ { // Merge the two vectors.
+ auto records2 = ParseRecords(data2, size2);
+ if (records2.empty()) {
+ return 0;
+ }
+ std::move(records2.begin(), records2.end(), std::back_inserter(records1));
+ }
+
+ // Shuffle record order.
+ std::shuffle(records1.begin(), records1.end(), rng);
+
+ size_t total = 0;
+ for (auto &rec : records1) {
+ size_t length = rec->size();
+ if (total + length > max_out_size) {
+ break;
+ }
+
+ // Write record to its new position.
+ memcpy(out + total, rec->data(), length);
+ total += length;
+ }
+
+ return total;
+}
diff --git a/fuzz/tls_mutators.h b/fuzz/tls_mutators.h
new file mode 100644
index 000000000..48429626f
--- /dev/null
+++ b/fuzz/tls_mutators.h
@@ -0,0 +1,23 @@
+/* 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 tls_mutators_h__
+#define tls_mutators_h__
+
+size_t TlsMutatorDropRecord(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed);
+size_t TlsMutatorShuffleRecords(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed);
+size_t TlsMutatorDuplicateRecord(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed);
+size_t TlsMutatorTruncateRecord(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed);
+size_t TlsMutatorFragmentRecord(uint8_t *data, size_t size, size_t max_size,
+ unsigned int seed);
+
+size_t TlsCrossOver(const uint8_t *data1, size_t size1, const uint8_t *data2,
+ size_t size2, uint8_t *out, size_t max_out_size,
+ unsigned int seed);
+
+#endif // tls_mutators_h__
diff --git a/fuzz/tls_server_target.cc b/fuzz/tls_server_target.cc
index 9a533fb13..67193368f 100644
--- a/fuzz/tls_server_target.cc
+++ b/fuzz/tls_server_target.cc
@@ -12,6 +12,7 @@
#include "shared.h"
#include "tls_common.h"
+#include "tls_mutators.h"
#include "tls_server_certs.h"
#include "tls_server_config.h"
#include "tls_socket.h"
@@ -83,10 +84,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) {
// Clear the cache. We never want to resume as we couldn't reproduce that.
SSL_ClearSessionCache();
-#ifdef UNSAFE_FUZZER_MODE
// Reset the RNG state.
assert(RNG_RandomUpdate(NULL, 0) == SECSuccess);
-#endif
// Create model socket.
static ScopedPRFileDesc model(SSL_ImportFD(nullptr, PR_NewTCPSocket()));
@@ -108,3 +107,18 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) {
return 0;
}
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
+ size_t max_size, unsigned int seed) {
+ return CustomMutate({TlsMutatorDropRecord, TlsMutatorShuffleRecords,
+ TlsMutatorDuplicateRecord, TlsMutatorTruncateRecord,
+ TlsMutatorFragmentRecord},
+ data, size, max_size, seed);
+}
+
+extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t* data1, size_t size1,
+ const uint8_t* data2, size_t size2,
+ uint8_t* out, size_t max_out_size,
+ unsigned int seed) {
+ return TlsCrossOver(data1, size1, data2, size2, out, max_out_size, seed);
+}