Bug 1351600 - Add DTLS client and server fuzzers r=franziskus
authorTim Taubert <ttaubert@mozilla.com>
Fri, 31 Mar 2017 16:32:54 +0200
changeset 13262 1fb7e5f584de
parent 13261 215207b4864c
child 13263 9a79b408169f
push id2128
push userttaubert@mozilla.com
push dateFri, 31 Mar 2017 14:33:20 +0000
reviewersfranziskus
bugs1351600
Bug 1351600 - Add DTLS client and server fuzzers r=franziskus Differential Revision: https://nss-review.dev.mozaws.net/D282
automation/ossfuzz/build.sh
automation/taskcluster/graph/src/extend.js
fuzz/fuzz.gyp
fuzz/options/dtls-client-no_fuzzer_mode.options
fuzz/options/dtls-client.options
fuzz/options/dtls-server-no_fuzzer_mode.options
fuzz/options/dtls-server.options
fuzz/tls_client_target.cc
fuzz/tls_mutators.cc
fuzz/tls_mutators.h
fuzz/tls_server_target.cc
--- a/automation/ossfuzz/build.sh
+++ b/automation/ossfuzz/build.sh
@@ -5,17 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 #
 ################################################################################
 
 # List of targets disabled for oss-fuzz.
 declare -A disabled=([pkcs8]=1)
 
 # List of targets we want to fuzz in TLS and non-TLS mode.
-declare -A tls_targets=([tls-client]=1 [tls-server]=1)
+declare -A tls_targets=([tls-client]=1 [tls-server]=1 [dtls-client]=1 [dtls-server]=1)
 
 # Helper function that copies a fuzzer binary and its seed corpus.
 copy_fuzzer()
 {
     local fuzzer=$1
     local name=$2
 
     # Copy the binary.
--- a/automation/taskcluster/graph/src/extend.js
+++ b/automation/taskcluster/graph/src/extend.js
@@ -378,21 +378,27 @@ async function scheduleFuzzing() {
   scheduleFuzzingRun(mpi_base, `MPI (invmod)`, `mpi-invmod`, 256, "invmod");
 
   // Schedule TLS fuzzing runs (non-fuzzing mode).
   let tls_base = merge(run_base, {group: "TLS"});
   scheduleFuzzingRun(tls_base, "TLS Client", "tls-client", 20000, "client-nfm",
                      "tls-client-no_fuzzer_mode");
   scheduleFuzzingRun(tls_base, "TLS Server", "tls-server", 20000, "server-nfm",
                      "tls-server-no_fuzzer_mode");
+  scheduleFuzzingRun(tls_base, "DTLS Client", "dtls-client", 20000,
+                     "dtls-client-nfm", "dtls-client-no_fuzzer_mode");
+  scheduleFuzzingRun(tls_base, "DTLS Server", "dtls-server", 20000,
+                     "dtls-server-nfm", "dtls-server-no_fuzzer_mode");
 
   // Schedule TLS fuzzing runs (fuzzing mode).
   let tls_fm_base = merge(tls_base, {parent: task_build_tls});
   scheduleFuzzingRun(tls_fm_base, "TLS Client", "tls-client", 20000, "client");
   scheduleFuzzingRun(tls_fm_base, "TLS Server", "tls-server", 20000, "server");
+  scheduleFuzzingRun(tls_fm_base, "DTLS Client", "dtls-client", 20000, "dtls-client");
+  scheduleFuzzingRun(tls_fm_base, "DTLS Server", "dtls-server", 20000, "dtls-server");
 
   return queue.submit();
 }
 
 /*****************************************************************************/
 
 async function scheduleTestBuilds() {
   let base = {
--- a/fuzz/fuzz.gyp
+++ b/fuzz/fuzz.gyp
@@ -296,20 +296,55 @@
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
         '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',
         'nssfuzz-tls-server',
       ],
       'conditions': [
         ['OS=="linux"', {
           'dependencies': [
new file mode 100644
--- /dev/null
+++ b/fuzz/options/dtls-client-no_fuzzer_mode.options
@@ -0,0 +1,3 @@
+[libfuzzer]
+max_len = 20000
+
new file mode 100644
--- /dev/null
+++ b/fuzz/options/dtls-client.options
@@ -0,0 +1,3 @@
+[libfuzzer]
+max_len = 20000
+
new file mode 100644
--- /dev/null
+++ b/fuzz/options/dtls-server-no_fuzzer_mode.options
@@ -0,0 +1,3 @@
+[libfuzzer]
+max_len = 20000
+
new file mode 100644
--- /dev/null
+++ b/fuzz/options/dtls-server.options
@@ -0,0 +1,3 @@
+[libfuzzer]
+max_len = 20000
+
--- a/fuzz/tls_client_target.cc
+++ b/fuzz/tls_client_target.cc
@@ -11,16 +11,30 @@
 #include "ssl.h"
 
 #include "shared.h"
 #include "tls_client_config.h"
 #include "tls_common.h"
 #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);
   auto config = reinterpret_cast<ClientConfig*>(arg);
   return config->FailCertificateAuthentication() ? SECFailure : SECSuccess;
 }
 
 static void SetSocketOptions(PRFileDesc* fd,
@@ -44,19 +58,21 @@ static void SetSocketOptions(PRFileDesc*
 
   rv = SSL_OptionSet(fd, SSL_CBC_RANDOM_IV, config->EnableCbcRandomIv());
   assert(rv == SECSuccess);
 
   rv = SSL_OptionSet(fd, SSL_REQUIRE_SAFE_NEGOTIATION,
                      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,
 // so we can always just set *canFalseStart=true.
 static SECStatus CanFalseStartCallback(PRFileDesc* fd, void* arg,
                                        PRBool* canFalseStart) {
   *canFalseStart = true;
   return SECSuccess;
@@ -82,36 +98,37 @@ extern "C" int LLVMFuzzerTestOneInput(co
 
   // Reset the RNG state.
   assert(RNG_RandomUpdate(NULL, 0) == SECSuccess);
 
   // Create and import dummy socket.
   std::unique_ptr<DummyPrSocket> 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.
   SSL_SetURL(ssl_fd, "server");
 
   SetSocketOptions(ssl_fd, config);
   EnableAllCipherSuites(ssl_fd);
   SetupCallbacks(ssl_fd, config.get());
   DoHandshake(ssl_fd, false);
 
   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},
+  using namespace TlsMutators;
+  return CustomMutate({DropRecord, ShuffleRecords, DuplicateRecord,
+                       TruncateRecord, FragmentRecord},
                       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);
+  return TlsMutators::CrossOver(data1, size1, data2, size2, out, max_out_size,
+                                seed);
 }
--- a/fuzz/tls_mutators.cc
+++ b/fuzz/tls_mutators.cc
@@ -5,16 +5,20 @@
 #include "shared.h"
 #include "tls_parser.h"
 
 #include "ssl.h"
 #include "sslimpl.h"
 
 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:
   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));
   }
 
@@ -28,19 +32,19 @@ class Record {
     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);
+    assert(length >= 5 + gExtraHeaderBytes);
     uint8_t *dest = const_cast<uint8_t *>(data_);
-    (void)ssl_EncodeUintX(length - 5, 2, &dest[3]);
+    (void)ssl_EncodeUintX(length - 5 - gExtraHeaderBytes, 2, &dest[3]);
     memmove(dest + length, data_ + size_, remaining_);
   }
 
   void drop() {
     uint8_t *dest = const_cast<uint8_t *>(data_);
     memmove(dest, data_ + size_, remaining_);
   }
 
@@ -61,41 +65,42 @@ class Record {
 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)) {
+    // Skip type, version, and DTLS seqnums.
+    if (!parser.Skip(3 + gExtraHeaderBytes)) {
       break;
     }
 
     DataBuffer fragment;
     if (!parser.ReadVariable(&fragment, 2)) {
       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.
   auto records = ParseRecords(data, size);
   if (records.empty()) {
     return 0;
   }
 
@@ -106,18 +111,18 @@ size_t TlsMutatorDropRecord(uint8_t *dat
   // 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) {
+size_t ShuffleRecords(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));
@@ -137,18 +142,18 @@ size_t TlsMutatorShuffleRecords(uint8_t 
     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) {
+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.
   const auto records = ParseRecords(data, size);
   if (records.empty()) {
     return 0;
   }
 
@@ -162,49 +167,55 @@ size_t TlsMutatorDuplicateRecord(uint8_t
   // 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) {
+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.
   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) {
+  if (rec->size() <= 5 + gExtraHeaderBytes) {
     return 0;
   }
 
   // Truncate.
-  std::uniform_int_distribution<size_t> dist2(5, rec->size() - 1);
+  std::uniform_int_distribution<size_t> dist2(5 + gExtraHeaderBytes,
+                                              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) {
+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;
   }
 
   // Find TLS records in the corpus.
   const auto records = ParseRecords(data, size);
   if (records.empty()) {
     return 0;
@@ -233,19 +244,19 @@ size_t TlsMutatorFragmentRecord(uint8_t 
   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) {
+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.
   auto records1 = ParseRecords(data1, size1);
   if (records1.empty()) {
     return 0;
   }
 
@@ -269,8 +280,10 @@ size_t TlsCrossOver(const uint8_t *data1
 
     // Write record to its new position.
     memcpy(out + total, rec->data(), length);
     total += length;
   }
 
   return total;
 }
+
+}  // namespace TlsMutators
--- a/fuzz/tls_mutators.h
+++ b/fuzz/tls_mutators.h
@@ -1,23 +1,29 @@
 /* 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);
+namespace TlsMutators {
+
+void SetIsDTLS();
 
-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 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__
--- a/fuzz/tls_server_target.cc
+++ b/fuzz/tls_server_target.cc
@@ -12,16 +12,30 @@
 
 #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"
 
+#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() {
     assert(SSL_ConfigServerSessionIDCache(1024, 0, 0, ".") == SECSuccess);
   }
 
   ~SSLServerSessionCache() {
     assert(SSL_ShutdownServerSessionIDCache() == SECSuccess);
@@ -51,19 +65,21 @@ static void SetSocketOptions(PRFileDesc*
 
   rv = SSL_OptionSet(fd, SSL_CBC_RANDOM_IV, config->EnableCbcRandomIv());
   assert(rv == SECSuccess);
 
   rv = SSL_OptionSet(fd, SSL_REQUIRE_SAFE_NEGOTIATION,
                      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) {
   PRFileDesc* fd = reinterpret_cast<PRFileDesc*>(arg);
 
   EnableAllProtocolVersions();
   EnableAllCipherSuites(fd);
   InstallServerCertificates(fd);
@@ -83,42 +99,43 @@ extern "C" int LLVMFuzzerTestOneInput(co
 
   // Clear the cache. We never want to resume as we couldn't reproduce that.
   SSL_ClearSessionCache();
 
   // Reset the RNG state.
   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.
   static PRCallOnceType initModelOnce;
   PR_CallOnceWithArg(&initModelOnce, InitModelSocket, model.get());
 
   // Create and import dummy socket.
   std::unique_ptr<DummyPrSocket> 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);
   DoHandshake(ssl_fd, true);
 
   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},
+  using namespace TlsMutators;
+  return CustomMutate({DropRecord, ShuffleRecords, DuplicateRecord,
+                       TruncateRecord, FragmentRecord},
                       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);
+  return TlsMutators::CrossOver(data1, size1, data2, size2, out, max_out_size,
+                                seed);
 }