diff options
author | Tim Taubert <ttaubert@mozilla.com> | 2017-03-24 11:18:52 +0100 |
---|---|---|
committer | Tim Taubert <ttaubert@mozilla.com> | 2017-03-24 11:18:52 +0100 |
commit | 14dce5c17c2b2993ffd4fa456524b781f46a2dc6 (patch) | |
tree | 31724255437e0c38c5aba50459f21ea0d797129f /fuzz | |
parent | 81311a26f78c579d457a1d0e04f7e7e382e93235 (diff) | |
download | nss-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.gyp | 56 | ||||
-rw-r--r-- | fuzz/pkcs8_target.cc | 10 | ||||
-rw-r--r-- | fuzz/quickder_target.cc | 10 | ||||
-rw-r--r-- | fuzz/shared.cc | 18 | ||||
-rw-r--r-- | fuzz/shared.h | 14 | ||||
-rw-r--r-- | fuzz/tls_client_target.cc | 18 | ||||
-rw-r--r-- | fuzz/tls_mutators.cc | 276 | ||||
-rw-r--r-- | fuzz/tls_mutators.h | 23 | ||||
-rw-r--r-- | fuzz/tls_server_target.cc | 18 |
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); +} |