Bug 1348775 - Add custom TLS mutators for libFuzzer fuzzing targets r=franziskus
authorTim Taubert <ttaubert@mozilla.com>
Fri, 24 Mar 2017 11:18:52 +0100
changeset 13249 3bcb0acab9260df0e20fcd6a8335cc19dae2b933
parent 13248 06158d335df0739f9e6a3d864b064694009d0557
child 13250 0985b120ab0247bdcbf340566ccbde69745cc22f
push id2117
push userttaubert@mozilla.com
push dateFri, 24 Mar 2017 10:19:28 +0000
reviewersfranziskus
bugs1348775
Bug 1348775 - Add custom TLS mutators for libFuzzer fuzzing targets r=franziskus Differential Revision: https://nss-review.dev.mozaws.net/D261
cpputil/cpputil.gyp
cpputil/databuffer.h
cpputil/tls_parser.cc
cpputil/tls_parser.h
fuzz/fuzz.gyp
fuzz/pkcs8_target.cc
fuzz/quickder_target.cc
fuzz/shared.cc
fuzz/shared.h
fuzz/tls_client_target.cc
fuzz/tls_mutators.cc
fuzz/tls_mutators.h
fuzz/tls_server_target.cc
gtests/ssl_gtest/databuffer.h
gtests/ssl_gtest/manifest.mn
gtests/ssl_gtest/ssl_gtest.gyp
gtests/ssl_gtest/tls_parser.cc
gtests/ssl_gtest/tls_parser.h
--- a/cpputil/cpputil.gyp
+++ b/cpputil/cpputil.gyp
@@ -7,16 +7,17 @@
   ],
   'targets': [
     {
       'target_name': 'cpputil',
       'type': 'static_library',
       'sources': [
         'dummy_io.cc',
         'dummy_io_fwd.cc',
+        'tls_parser.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
           '<(DEPTH)/cpputil',
         ],
rename from gtests/ssl_gtest/databuffer.h
rename to cpputil/databuffer.h
rename from gtests/ssl_gtest/tls_parser.cc
rename to cpputil/tls_parser.cc
rename from gtests/ssl_gtest/tls_parser.h
rename to cpputil/tls_parser.h
--- a/fuzz/fuzz.gyp
+++ b/fuzz/fuzz.gyp
@@ -20,46 +20,49 @@
           '<(nss_dist_obj_dir)/lib/libplc4.a',
         ],
       }],
     ],
   },
   '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',
         '<(DEPTH)/lib/ssl/ssl.gyp:ssl',
         '<(DEPTH)/lib/base/base.gyp:nssb',
         '<(DEPTH)/lib/dev/dev.gyp:nssdev',
         '<(DEPTH)/lib/pki/pki.gyp:nsspki',
         '<(DEPTH)/lib/util/util.gyp:nssutil',
         '<(DEPTH)/lib/nss/nss.gyp:nss_static',
         '<(DEPTH)/lib/pkcs7/pkcs7.gyp:pkcs7',
         # This is a static build of pk11wrap, softoken, and freebl.
         '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
       ],
       'conditions': [
         ['fuzz_oss==0', {
-          'type': 'static_library',
           'sources': [
             '<!@(ls <(DEPTH)/fuzz/libFuzzer/*.cpp)',
           ],
           'cflags/': [
             ['exclude', '-fsanitize-coverage'],
           ],
           'xcode_settings': {
             'OTHER_CFLAGS/': [
               ['exclude', '-fsanitize-coverage'],
             ],
           },
         }, {
-          'type': 'none',
           'all_dependent_settings': {
             'libraries': ['-lFuzzingEngine'],
           }
         }]
       ],
     },
     {
       'target_name': 'nssfuzz-mpi-base',
@@ -243,64 +246,63 @@
         '<(DEPTH)/exports.gyp:nss_exports',
         'nssfuzz-mpi-base',
       ],
       'include_dirs': [
         '<(DEPTH)/lib/freebl',
       ],
     },
     {
-      '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': [
         '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
         '<(DEPTH)/exports.gyp:nss_exports',
         '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)/exports.gyp:nss_exports',
         '<(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',
-          ],
-        }],
+        'nssfuzz-tls-base',
       ],
     },
     {
       'target_name': 'nssfuzz',
       'type': 'none',
       'dependencies': [
         'nssfuzz-certDN',
         'nssfuzz-pkcs8',
--- a/fuzz/pkcs8_target.cc
+++ b/fuzz/pkcs8_target.cc
@@ -26,14 +26,14 @@ extern "C" int LLVMFuzzerTestOneInput(co
                                                nullptr) == SECSuccess) {
     SECKEY_DestroyPrivateKey(key);
   }
 
   PK11_FreeSlot(slot);
   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);
 }
--- a/fuzz/quickder_target.cc
+++ b/fuzz/quickder_target.cc
@@ -72,14 +72,14 @@ extern "C" int LLVMFuzzerTestOneInput(co
     PORT_InitCheapArena(&pool, DER_DEFAULT_CHUNKSIZE);
     (void)SEC_QuickDERDecodeItem(&pool.arena, dest, tpl, &buf);
     PORT_DestroyCheapArena(&pool);
   }
 
   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);
 }
new file mode 100644
--- /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);
+}
--- a/fuzz/shared.h
+++ b/fuzz/shared.h
@@ -19,22 +19,12 @@ extern "C" size_t LLVMFuzzerCustomMutato
 class NSSDatabase {
  public:
   NSSDatabase() { assert(NSS_NoDB_Init(nullptr) == SECSuccess); }
   ~NSSDatabase() { assert(NSS_Shutdown() == SECSuccess); }
 };
 
 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__
--- a/fuzz/tls_client_target.cc
+++ b/fuzz/tls_client_target.cc
@@ -8,16 +8,17 @@
 
 #include "blapi.h"
 #include "prinit.h"
 #include "ssl.h"
 
 #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,
                                      PRBool isServer) {
   assert(!isServer);
   auto config = reinterpret_cast<ClientConfig*>(arg);
   return config->FailCertificateAuthentication() ? SECFailure : SECSuccess;
 }
@@ -74,20 +75,18 @@ extern "C" int LLVMFuzzerTestOneInput(co
   assert(db != nullptr);
 
   EnableAllProtocolVersions();
   std::unique_ptr<ClientConfig> config(new ClientConfig(data, 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));
   static PRDescIdentity id = PR_GetUniqueIdentity("fuzz-client");
   ScopedPRFileDesc fd(DummyIOLayerMethods::CreateFD(id, socket.get()));
   PRFileDesc* ssl_fd = SSL_ImportFD(nullptr, fd.get());
   assert(ssl_fd == fd.get());
 
@@ -96,8 +95,23 @@ extern "C" int LLVMFuzzerTestOneInput(co
 
   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},
+                      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);
+}
new file mode 100644
--- /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;
+}
new file mode 100644
--- /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__
--- a/fuzz/tls_server_target.cc
+++ b/fuzz/tls_server_target.cc
@@ -7,16 +7,17 @@
 #include <memory>
 
 #include "blapi.h"
 #include "prinit.h"
 #include "ssl.h"
 
 #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"
 
 class SSLServerSessionCache {
  public:
   SSLServerSessionCache() {
     assert(SSL_ConfigServerSessionIDCache(1024, 0, 0, ".") == SECSuccess);
@@ -78,20 +79,18 @@ extern "C" int LLVMFuzzerTestOneInput(co
       new SSLServerSessionCache());
   assert(cache != nullptr);
 
   std::unique_ptr<ServerConfig> config(new ServerConfig(data, 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()));
   assert(model);
 
   // Initialize the model socket once.
   static PRCallOnceType initModelOnce;
   PR_CallOnceWithArg(&initModelOnce, InitModelSocket, model.get());
@@ -103,8 +102,23 @@ extern "C" int LLVMFuzzerTestOneInput(co
   PRFileDesc* ssl_fd = SSL_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},
+                      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);
+}
--- a/gtests/ssl_gtest/manifest.mn
+++ b/gtests/ssl_gtest/manifest.mn
@@ -9,16 +9,17 @@ MODULE = nss
 # These sources have access to libssl internals
 CSRCS = \
       libssl_internals.c \
       $(NULL)
 
 CPPSRCS = \
       $(CORE_DEPTH)/cpputil/dummy_io.cc \
       $(CORE_DEPTH)/cpputil/dummy_io_fwd.cc \
+      $(CORE_DEPTH)/cpputil/tls_parser.cc \
       ssl_0rtt_unittest.cc \
       ssl_agent_unittest.cc \
       ssl_auth_unittest.cc \
       ssl_cert_ext_unittest.cc \
       ssl_ciphersuite_unittest.cc \
       ssl_damage_unittest.cc \
       ssl_dhe_unittest.cc \
       ssl_drop_unittest.cc \
@@ -38,17 +39,16 @@ CPPSRCS = \
       ssl_staticrsa_unittest.cc \
       ssl_v2_client_hello_unittest.cc \
       ssl_version_unittest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_hkdf_unittest.cc \
       tls_filter.cc \
-      tls_parser.cc \
       tls_protect.cc \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
             -I$(CORE_DEPTH)/gtests/common \
             -I$(CORE_DEPTH)/cpputil
 
 REQUIRES = nspr nss libdbm gtest
--- a/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/gtests/ssl_gtest/ssl_gtest.gyp
@@ -36,17 +36,16 @@
         'ssl_staticrsa_unittest.cc',
         'ssl_v2_client_hello_unittest.cc',
         'ssl_version_unittest.cc',
         'test_io.cc',
         'tls_agent.cc',
         'tls_connect.cc',
         'tls_filter.cc',
         'tls_hkdf_unittest.cc',
-        'tls_parser.cc',
         'tls_protect.cc'
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/util/util.gyp:nssutil3',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/smime/smime.gyp:smime',
         '<(DEPTH)/lib/ssl/ssl.gyp:ssl',