From a1d6082c30f4bfea3f08a1c698a3e39adfd2f94a Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Fri, 31 Mar 2017 16:32:54 +0200 Subject: Bug 1351600 - Add DTLS client and server fuzzers r=franziskus Differential Revision: https://nss-review.dev.mozaws.net/D282 --- fuzz/fuzz.gyp | 35 +++++++++++++ fuzz/options/dtls-client-no_fuzzer_mode.options | 3 ++ fuzz/options/dtls-client.options | 3 ++ fuzz/options/dtls-server-no_fuzzer_mode.options | 3 ++ fuzz/options/dtls-server.options | 3 ++ fuzz/tls_client_target.cc | 27 ++++++++-- fuzz/tls_mutators.cc | 65 +++++++++++++++---------- fuzz/tls_mutators.h | 32 +++++++----- fuzz/tls_server_target.cc | 29 ++++++++--- 9 files changed, 150 insertions(+), 50 deletions(-) create mode 100644 fuzz/options/dtls-client-no_fuzzer_mode.options create mode 100644 fuzz/options/dtls-client.options create mode 100644 fuzz/options/dtls-server-no_fuzzer_mode.options create mode 100644 fuzz/options/dtls-server.options (limited to 'fuzz') diff --git a/fuzz/fuzz.gyp b/fuzz/fuzz.gyp index da52d6e37..a7339b78c 100644 --- a/fuzz/fuzz.gyp +++ b/fuzz/fuzz.gyp @@ -300,11 +300,46 @@ 'nssfuzz-tls-base', ], }, + { + 'target_name': 'nssfuzz-dtls-client', + 'type': 'executable', + 'sources': [ + 'tls_client_config.cc', + 'tls_client_target.cc', + ], + 'defines': [ + 'IS_DTLS' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports', + '<(DEPTH)/cpputil/cpputil.gyp:cpputil', + 'nssfuzz-tls-base', + ], + }, + { + 'target_name': 'nssfuzz-dtls-server', + 'type': 'executable', + 'sources': [ + 'tls_server_certs.cc', + 'tls_server_config.cc', + 'tls_server_target.cc', + ], + 'defines': [ + 'IS_DTLS' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports', + '<(DEPTH)/cpputil/cpputil.gyp:cpputil', + 'nssfuzz-tls-base', + ], + }, { 'target_name': 'nssfuzz', 'type': 'none', 'dependencies': [ 'nssfuzz-certDN', + 'nssfuzz-dtls-client', + 'nssfuzz-dtls-server', 'nssfuzz-pkcs8', 'nssfuzz-quickder', 'nssfuzz-tls-client', diff --git a/fuzz/options/dtls-client-no_fuzzer_mode.options b/fuzz/options/dtls-client-no_fuzzer_mode.options new file mode 100644 index 000000000..8b017d2ce --- /dev/null +++ b/fuzz/options/dtls-client-no_fuzzer_mode.options @@ -0,0 +1,3 @@ +[libfuzzer] +max_len = 20000 + diff --git a/fuzz/options/dtls-client.options b/fuzz/options/dtls-client.options new file mode 100644 index 000000000..8b017d2ce --- /dev/null +++ b/fuzz/options/dtls-client.options @@ -0,0 +1,3 @@ +[libfuzzer] +max_len = 20000 + diff --git a/fuzz/options/dtls-server-no_fuzzer_mode.options b/fuzz/options/dtls-server-no_fuzzer_mode.options new file mode 100644 index 000000000..8b017d2ce --- /dev/null +++ b/fuzz/options/dtls-server-no_fuzzer_mode.options @@ -0,0 +1,3 @@ +[libfuzzer] +max_len = 20000 + diff --git a/fuzz/options/dtls-server.options b/fuzz/options/dtls-server.options new file mode 100644 index 000000000..8b017d2ce --- /dev/null +++ b/fuzz/options/dtls-server.options @@ -0,0 +1,3 @@ +[libfuzzer] +max_len = 20000 + diff --git a/fuzz/tls_client_target.cc b/fuzz/tls_client_target.cc index 82e4a5dfb..e59550984 100644 --- a/fuzz/tls_client_target.cc +++ b/fuzz/tls_client_target.cc @@ -16,6 +16,20 @@ #include "tls_mutators.h" #include "tls_socket.h" +#ifdef IS_DTLS +__attribute__((constructor)) static void set_is_dtls() { + TlsMutators::SetIsDTLS(); +} +#endif + +PRFileDesc* ImportFD(PRFileDesc* model, PRFileDesc* fd) { +#ifdef IS_DTLS + return DTLS_ImportFD(model, fd); +#else + return SSL_ImportFD(model, fd); +#endif +} + static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checksig, PRBool isServer) { assert(!isServer); @@ -49,9 +63,11 @@ static void SetSocketOptions(PRFileDesc* fd, config->RequireSafeNegotiation()); assert(rv == SECSuccess); +#ifndef IS_DTLS rv = SSL_OptionSet(fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_UNRESTRICTED); assert(rv == SECSuccess); +#endif } // This is only called when we set SSL_ENABLE_FALSE_START=1, @@ -87,7 +103,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) { std::unique_ptr socket(new DummyPrSocket(data, len)); static PRDescIdentity id = PR_GetUniqueIdentity("fuzz-client"); ScopedPRFileDesc fd(DummyIOLayerMethods::CreateFD(id, socket.get())); - PRFileDesc* ssl_fd = SSL_ImportFD(nullptr, fd.get()); + PRFileDesc* ssl_fd = ImportFD(nullptr, fd.get()); assert(ssl_fd == fd.get()); // Probably not too important for clients. @@ -103,9 +119,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) { 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}, + using namespace TlsMutators; + return CustomMutate({DropRecord, ShuffleRecords, DuplicateRecord, + TruncateRecord, FragmentRecord}, data, size, max_size, seed); } @@ -113,5 +129,6 @@ 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); + return TlsMutators::CrossOver(data1, size1, data2, size2, out, max_out_size, + seed); } diff --git a/fuzz/tls_mutators.cc b/fuzz/tls_mutators.cc index ccf1935bf..e9770cb39 100644 --- a/fuzz/tls_mutators.cc +++ b/fuzz/tls_mutators.cc @@ -10,6 +10,10 @@ using namespace nss_test; +// Number of additional bytes in the TLS header. +// Used to properly skip DTLS seqnums. +static size_t gExtraHeaderBytes = 0; + // Helper class to simplify TLS record manipulation. class Record { public: @@ -33,9 +37,9 @@ class Record { } void truncate(size_t length) { - assert(length >= 5); + assert(length >= 5 + gExtraHeaderBytes); uint8_t *dest = const_cast(data_); - (void)ssl_EncodeUintX(length - 5, 2, &dest[3]); + (void)ssl_EncodeUintX(length - 5 - gExtraHeaderBytes, 2, &dest[3]); memmove(dest + length, data_ + size_, remaining_); } @@ -66,13 +70,8 @@ std::vector> ParseRecords(const uint8_t *data, 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)) { + // Skip type, version, and DTLS seqnums. + if (!parser.Skip(3 + gExtraHeaderBytes)) { break; } @@ -81,16 +80,22 @@ std::vector> ParseRecords(const uint8_t *data, break; } - records.push_back( - Record::Create(data + offset, fragment.len() + 5, parser.remaining())); + records.push_back(Record::Create(data + offset, + fragment.len() + 5 + gExtraHeaderBytes, + parser.remaining())); } return records; } +namespace TlsMutators { + +// Handle seqnums in DTLS transcripts. +void SetIsDTLS() { gExtraHeaderBytes = 8; } + // Mutator that drops whole TLS records. -size_t TlsMutatorDropRecord(uint8_t *data, size_t size, size_t max_size, - unsigned int seed) { +size_t DropRecord(uint8_t *data, size_t size, size_t max_size, + unsigned int seed) { std::mt19937 rng(seed); // Find TLS records in the corpus. @@ -111,8 +116,8 @@ size_t TlsMutatorDropRecord(uint8_t *data, size_t size, size_t max_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) { +size_t ShuffleRecords(uint8_t *data, size_t size, size_t max_size, + unsigned int seed) { std::mt19937 rng(seed); // Store the original corpus. @@ -142,8 +147,8 @@ size_t TlsMutatorShuffleRecords(uint8_t *data, size_t size, size_t max_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) { +size_t DuplicateRecord(uint8_t *data, size_t size, size_t max_size, + unsigned int seed) { std::mt19937 rng(seed); // Find TLS records in the corpus. @@ -167,8 +172,8 @@ size_t TlsMutatorDuplicateRecord(uint8_t *data, size_t size, size_t max_size, } // Mutator that truncates a TLS record. -size_t TlsMutatorTruncateRecord(uint8_t *data, size_t size, size_t max_size, - unsigned int seed) { +size_t TruncateRecord(uint8_t *data, size_t size, size_t max_size, + unsigned int seed) { std::mt19937 rng(seed); // Find TLS records in the corpus. @@ -182,12 +187,13 @@ size_t TlsMutatorTruncateRecord(uint8_t *data, size_t size, size_t max_size, auto &rec = records.at(dist(rng)); // Need a record with data. - if (rec->size() <= 5) { + if (rec->size() <= 5 + gExtraHeaderBytes) { return 0; } // Truncate. - std::uniform_int_distribution dist2(5, rec->size() - 1); + std::uniform_int_distribution dist2(5 + gExtraHeaderBytes, + rec->size() - 1); size_t new_length = dist2(rng); rec->truncate(new_length); @@ -196,10 +202,15 @@ size_t TlsMutatorTruncateRecord(uint8_t *data, size_t size, size_t max_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) { +size_t FragmentRecord(uint8_t *data, size_t size, size_t max_size, + unsigned int seed) { std::mt19937 rng(seed); + // We can't deal with DTLS yet. + if (gExtraHeaderBytes > 0) { + return 0; + } + if (size + 5 > max_size) { return 0; } @@ -238,9 +249,9 @@ size_t TlsMutatorFragmentRecord(uint8_t *data, size_t size, size_t max_size, } // 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) { +size_t CrossOver(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. @@ -274,3 +285,5 @@ size_t TlsCrossOver(const uint8_t *data1, size_t size1, const uint8_t *data2, return total; } + +} // namespace TlsMutators diff --git a/fuzz/tls_mutators.h b/fuzz/tls_mutators.h index 48429626f..03a214751 100644 --- a/fuzz/tls_mutators.h +++ b/fuzz/tls_mutators.h @@ -5,19 +5,25 @@ #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); +namespace TlsMutators { -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); +void SetIsDTLS(); + +size_t DropRecord(uint8_t *data, size_t size, size_t max_size, + unsigned int seed); +size_t ShuffleRecords(uint8_t *data, size_t size, size_t max_size, + unsigned int seed); +size_t DuplicateRecord(uint8_t *data, size_t size, size_t max_size, + unsigned int seed); +size_t TruncateRecord(uint8_t *data, size_t size, size_t max_size, + unsigned int seed); +size_t FragmentRecord(uint8_t *data, size_t size, size_t max_size, + unsigned int seed); + +size_t CrossOver(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); + +} // namespace TlsMutators #endif // tls_mutators_h__ diff --git a/fuzz/tls_server_target.cc b/fuzz/tls_server_target.cc index 67193368f..0c0902077 100644 --- a/fuzz/tls_server_target.cc +++ b/fuzz/tls_server_target.cc @@ -17,6 +17,20 @@ #include "tls_server_config.h" #include "tls_socket.h" +#ifdef IS_DTLS +__attribute__((constructor)) static void set_is_dtls() { + TlsMutators::SetIsDTLS(); +} +#endif + +PRFileDesc* ImportFD(PRFileDesc* model, PRFileDesc* fd) { +#ifdef IS_DTLS + return DTLS_ImportFD(model, fd); +#else + return SSL_ImportFD(model, fd); +#endif +} + class SSLServerSessionCache { public: SSLServerSessionCache() { @@ -56,9 +70,11 @@ static void SetSocketOptions(PRFileDesc* fd, config->RequireSafeNegotiation()); assert(rv == SECSuccess); +#ifndef IS_DTLS rv = SSL_OptionSet(fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_UNRESTRICTED); assert(rv == SECSuccess); +#endif } static PRStatus InitModelSocket(void* arg) { @@ -88,7 +104,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) { assert(RNG_RandomUpdate(NULL, 0) == SECSuccess); // Create model socket. - static ScopedPRFileDesc model(SSL_ImportFD(nullptr, PR_NewTCPSocket())); + static ScopedPRFileDesc model(ImportFD(nullptr, PR_NewTCPSocket())); assert(model); // Initialize the model socket once. @@ -99,7 +115,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) { std::unique_ptr socket(new DummyPrSocket(data, len)); static PRDescIdentity id = PR_GetUniqueIdentity("fuzz-server"); ScopedPRFileDesc fd(DummyIOLayerMethods::CreateFD(id, socket.get())); - PRFileDesc* ssl_fd = SSL_ImportFD(model.get(), fd.get()); + PRFileDesc* ssl_fd = ImportFD(model.get(), fd.get()); assert(ssl_fd == fd.get()); SetSocketOptions(ssl_fd, config); @@ -110,9 +126,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) { 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}, + using namespace TlsMutators; + return CustomMutate({DropRecord, ShuffleRecords, DuplicateRecord, + TruncateRecord, FragmentRecord}, data, size, max_size, seed); } @@ -120,5 +136,6 @@ 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); + return TlsMutators::CrossOver(data1, size1, data2, size2, out, max_out_size, + seed); } -- cgit v1.2.1