Bug 1144055 - Upgrade Firefox 39 to use NSS 3.18.1, land NSS_3_18_1_BETA1, r=nss-confcall
authorKai Engert <kaie@kuix.de>
Thu, 26 Mar 2015 20:39:25 +0100
changeset 236005 0db3b8dc05e2e8b2ad372a4b7bbe23fa85827b99
parent 236004 50bbee4a42606646d11394f1ea8f13e1a472d73e
child 236006 31df2186195a474ae202f33a6bfef3167a0190dc
push id28488
push userryanvm@gmail.com
push dateFri, 27 Mar 2015 16:19:11 +0000
treeherdermozilla-central@44e454b5e93b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnss-confcall
bugs1144055
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1144055 - Upgrade Firefox 39 to use NSS 3.18.1, land NSS_3_18_1_BETA1, r=nss-confcall
security/nss/TAG-INFO
security/nss/cmd/platlibs.mk
security/nss/coreconf/coreconf.dep
security/nss/coreconf/location.mk
security/nss/external_tests/ssl_gtest/manifest.mn
security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
security/nss/external_tests/ssl_gtest/test_io.cc
security/nss/external_tests/ssl_gtest/test_io.h
security/nss/external_tests/ssl_gtest/tls_agent.cc
security/nss/external_tests/ssl_gtest/tls_agent.h
security/nss/external_tests/ssl_gtest/tls_connect.cc
security/nss/external_tests/ssl_gtest/tls_connect.h
security/nss/external_tests/ssl_gtest/tls_filter.cc
security/nss/lib/nss/nss.h
security/nss/lib/softoken/config.mk
security/nss/lib/softoken/softkver.h
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3ecc.c
security/nss/lib/ssl/ssl3ext.c
security/nss/lib/ssl/sslimpl.h
security/nss/lib/util/nssutil.h
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_18_RTM
+NSS_3_18_1_BETA1
--- a/security/nss/cmd/platlibs.mk
+++ b/security/nss/cmd/platlibs.mk
@@ -82,18 +82,18 @@ EXTRA_LIBS += \
 	$(DIST)/lib/$(LIB_PREFIX)certdb.$(LIB_SUFFIX) \
 	$(SOFTOKENLIB) \
 	$(CRYPTOLIB) \
 	$(DIST)/lib/$(LIB_PREFIX)nsspki.$(LIB_SUFFIX) \
 	$(DIST)/lib/$(LIB_PREFIX)nssdev.$(LIB_SUFFIX) \
 	$(DIST)/lib/$(LIB_PREFIX)nssb.$(LIB_SUFFIX) \
 	$(PKIXLIB) \
 	$(DBMLIB) \
-	$(DIST)/lib/$(LIB_PREFIX)$(SQLITE_LIB_NAME).$(LIB_SUFFIX) \
-	$(DIST)/lib/$(LIB_PREFIX)nssutil3.$(LIB_SUFFIX) \
+	$(SQLITE_LIB_DIR)/$(LIB_PREFIX)$(SQLITE_LIB_NAME).$(LIB_SUFFIX) \
+	$(NSSUTIL_LIB_DIR)/$(LIB_PREFIX)nssutil3.$(LIB_SUFFIX) \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.$(LIB_SUFFIX) \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.$(LIB_SUFFIX) \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)nspr4.$(LIB_SUFFIX) \
 	$(NULL)
 
 # $(PROGRAM) has NO explicit dependencies on $(OS_LIBS)
 #OS_LIBS += \
 	wsock32.lib \
@@ -130,17 +130,17 @@ EXTRA_LIBS += \
 
 ifeq ($(OS_ARCH), AIX) 
 EXTRA_SHARED_LIBS += -brtl 
 endif
 
 # $(PROGRAM) has NO explicit dependencies on $(EXTRA_SHARED_LIBS)
 # $(EXTRA_SHARED_LIBS) come before $(OS_LIBS), except on AIX.
 EXTRA_SHARED_LIBS += \
-	-L$(DIST)/lib \
+	-L$(SQLITE_LIB_DIR) \
 	-l$(SQLITE_LIB_NAME) \
 	-L$(NSSUTIL_LIB_DIR) \
 	-lnssutil3 \
 	-L$(NSPR_LIB_DIR) \
 	-lplc4 \
 	-lplds4 \
 	-lnspr4 \
 	$(NULL)
@@ -148,17 +148,17 @@ endif
 
 else # USE_STATIC_LIBS
 # can't do this in manifest.mn because OS_ARCH isn't defined there.
 ifeq ($(OS_ARCH), WINNT)
 
 # $(PROGRAM) has explicit dependencies on $(EXTRA_LIBS)
 EXTRA_LIBS += \
 	$(DIST)/lib/$(LIB_PREFIX)sectool.$(LIB_SUFFIX) \
-	$(DIST)/lib/$(IMPORT_LIB_PREFIX)nssutil3$(IMPORT_LIB_SUFFIX) \
+	$(NSSUTIL_LIB_DIR)/$(IMPORT_LIB_PREFIX)nssutil3$(IMPORT_LIB_SUFFIX) \
 	$(DIST)/lib/$(IMPORT_LIB_PREFIX)smime3$(IMPORT_LIB_SUFFIX) \
 	$(DIST)/lib/$(IMPORT_LIB_PREFIX)ssl3$(IMPORT_LIB_SUFFIX) \
 	$(DIST)/lib/$(IMPORT_LIB_PREFIX)nss3$(IMPORT_LIB_SUFFIX) \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4$(IMPORT_LIB_SUFFIX) \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4$(IMPORT_LIB_SUFFIX) \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)nspr4$(IMPORT_LIB_SUFFIX) \
 	$(NULL)
 
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/coreconf/location.mk
+++ b/security/nss/coreconf/location.mk
@@ -62,13 +62,17 @@ endif
 ifdef SOFTOKEN_INCLUDE_DIR
     INCLUDES += -I$(SOFTOKEN_INCLUDE_DIR)
 endif
 
 ifndef SOFTOKEN_LIB_DIR
     SOFTOKEN_LIB_DIR = $(DIST)/lib
 endif
 
+ifndef SQLITE_LIB_DIR
+    SQLITE_LIB_DIR = $(DIST)/lib
+endif
+
 ifndef SQLITE_LIB_NAME
     SQLITE_LIB_NAME = sqlite3
 endif
 
 MK_LOCATION = included
--- a/security/nss/external_tests/ssl_gtest/manifest.mn
+++ b/security/nss/external_tests/ssl_gtest/manifest.mn
@@ -3,16 +3,17 @@
 # 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/.
 CORE_DEPTH = ../..
 DEPTH      = ../..
 MODULE = nss
 
 CPPSRCS = \
       ssl_loopback_unittest.cc \
+      ssl_extension_unittest.cc \
       ssl_gtest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_filter.cc \
       tls_parser.cc \
       $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
@@ -0,0 +1,578 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "ssl.h"
+#include "sslproto.h"
+
+#include <memory>
+
+#include "tls_parser.h"
+#include "tls_filter.h"
+#include "tls_connect.h"
+
+namespace nss_test {
+
+class TlsExtensionFilter : public TlsHandshakeFilter {
+ protected:
+  virtual bool FilterHandshake(uint16_t version, uint8_t handshake_type,
+                               const DataBuffer& input, DataBuffer* output) {
+    if (handshake_type == kTlsHandshakeClientHello) {
+      TlsParser parser(input);
+      if (!FindClientHelloExtensions(parser, version)) {
+        return false;
+      }
+      return FilterExtensions(parser, input, output);
+    }
+    if (handshake_type == kTlsHandshakeServerHello) {
+      TlsParser parser(input);
+      if (!FindServerHelloExtensions(parser, version)) {
+        return false;
+      }
+      return FilterExtensions(parser, input, output);
+    }
+    return false;
+  }
+
+  virtual bool FilterExtension(uint16_t extension_type,
+                               const DataBuffer& input, DataBuffer* output) = 0;
+
+ public:
+  static bool FindClientHelloExtensions(TlsParser& parser, uint16_t version) {
+    if (!parser.Skip(2 + 32)) { // version + random
+      return false;
+    }
+    if (!parser.SkipVariable(1)) { // session ID
+      return false;
+    }
+    if (IsDtls(version) && !parser.SkipVariable(1)) { // DTLS cookie
+      return false;
+    }
+    if (!parser.SkipVariable(2)) { // cipher suites
+      return false;
+    }
+    if (!parser.SkipVariable(1)) { // compression methods
+      return false;
+    }
+    return true;
+  }
+
+  static bool FindServerHelloExtensions(TlsParser& parser, uint16_t version) {
+    if (!parser.Skip(2 + 32)) { // version + random
+      return false;
+    }
+    if (!parser.SkipVariable(1)) { // session ID
+      return false;
+    }
+    if (!parser.Skip(2)) { // cipher suite
+      return false;
+    }
+    if (NormalizeTlsVersion(version) <= SSL_LIBRARY_VERSION_TLS_1_2) {
+      if (!parser.Skip(1)) { // compression method
+        return false;
+      }
+    }
+    return true;
+  }
+
+ private:
+  bool FilterExtensions(TlsParser& parser,
+                        const DataBuffer& input, DataBuffer* output) {
+    size_t length_offset = parser.consumed();
+    uint32_t all_extensions;
+    if (!parser.Read(&all_extensions, 2)) {
+      return false; // no extensions, odd but OK
+    }
+    if (all_extensions != parser.remaining()) {
+      return false; // malformed
+    }
+
+    bool changed = false;
+
+    // Write out the start of the message.
+    output->Allocate(input.len());
+    output->Write(0, input.data(), parser.consumed());
+    size_t output_offset = parser.consumed();
+
+    while (parser.remaining()) {
+      uint32_t extension_type;
+      if (!parser.Read(&extension_type, 2)) {
+        return false; // malformed
+      }
+
+      // Copy extension type.
+      output->Write(output_offset, extension_type, 2);
+
+      DataBuffer extension;
+      if (!parser.ReadVariable(&extension, 2)) {
+        return false; // malformed
+      }
+      output_offset = ApplyFilter(static_cast<uint16_t>(extension_type), extension,
+                                  output, output_offset + 2, &changed);
+    }
+    output->Truncate(output_offset);
+
+    if (changed) {
+      size_t newlen = output->len() - length_offset - 2;
+      if (newlen >= 0x10000) {
+        return false; // bad: size increased too much
+      }
+      output->Write(length_offset, newlen, 2);
+    }
+    return changed;
+  }
+
+  size_t ApplyFilter(uint16_t extension_type, const DataBuffer& extension,
+                     DataBuffer* output, size_t offset, bool* changed) {
+    const DataBuffer* source = &extension;
+    DataBuffer filtered;
+    if (FilterExtension(extension_type, extension, &filtered) &&
+        filtered.len() < 0x10000) {
+      *changed = true;
+      std::cerr << "extension old: " << extension << std::endl;
+      std::cerr << "extension new: " << filtered << std::endl;
+      source = &filtered;
+    }
+
+    output->Write(offset, source->len(), 2);
+    output->Write(offset + 2, *source);
+    return offset + 2 + source->len();
+  }
+};
+
+class TlsExtensionTruncator : public TlsExtensionFilter {
+ public:
+  TlsExtensionTruncator(uint16_t extension, size_t length)
+      : extension_(extension), length_(length) {}
+  virtual bool FilterExtension(uint16_t extension_type,
+                               const DataBuffer& input, DataBuffer* output) {
+    if (extension_type != extension_) {
+      return false;
+    }
+    if (input.len() <= length_) {
+      return false;
+    }
+
+    output->Assign(input.data(), length_);
+    return true;
+  }
+ private:
+    uint16_t extension_;
+    size_t length_;
+};
+
+class TlsExtensionDamager : public TlsExtensionFilter {
+ public:
+  TlsExtensionDamager(uint16_t extension, size_t index)
+      : extension_(extension), index_(index) {}
+  virtual bool FilterExtension(uint16_t extension_type,
+                               const DataBuffer& input, DataBuffer* output) {
+    if (extension_type != extension_) {
+      return false;
+    }
+
+    *output = input;
+    output->data()[index_] += 73; // Increment selected for maximum damage
+    return true;
+  }
+ private:
+  uint16_t extension_;
+  size_t index_;
+};
+
+class TlsExtensionReplacer : public TlsExtensionFilter {
+ public:
+  TlsExtensionReplacer(uint16_t extension, const DataBuffer& data)
+      : extension_(extension), data_(data) {}
+  virtual bool FilterExtension(uint16_t extension_type,
+                               const DataBuffer& input, DataBuffer* output) {
+    if (extension_type != extension_) {
+      return false;
+    }
+
+    *output = data_;
+    return true;
+  }
+ private:
+  uint16_t extension_;
+  DataBuffer data_;
+};
+
+class TlsExtensionInjector : public TlsHandshakeFilter {
+ public:
+  TlsExtensionInjector(uint16_t ext, DataBuffer& data)
+      : extension_(ext), data_(data) {}
+
+  virtual bool FilterHandshake(uint16_t version, uint8_t handshake_type,
+                               const DataBuffer& input, DataBuffer* output) {
+    size_t offset;
+    if (handshake_type == kTlsHandshakeClientHello) {
+      TlsParser parser(input);
+      if (!TlsExtensionFilter::FindClientHelloExtensions(parser, version)) {
+        return false;
+      }
+      offset = parser.consumed();
+    } else if (handshake_type == kTlsHandshakeServerHello) {
+      TlsParser parser(input);
+      if (!TlsExtensionFilter::FindServerHelloExtensions(parser, version)) {
+        return false;
+      }
+      offset = parser.consumed();
+    } else {
+      return false;
+    }
+
+    *output = input;
+
+    std::cerr << "Pre:" << input << std::endl;
+    std::cerr << "Lof:" << offset << std::endl;
+
+    // Increase the size of the extensions.
+    uint16_t* len_addr = reinterpret_cast<uint16_t*>(output->data() + offset);
+    std::cerr << "L-p:" << ntohs(*len_addr) << std::endl;
+    *len_addr = htons(ntohs(*len_addr) + data_.len() + 4);
+    std::cerr << "L-i:" << ntohs(*len_addr) << std::endl;
+
+
+    // Insert the extension type and length.
+    DataBuffer type_length;
+    type_length.Allocate(4);
+    type_length.Write(0, extension_, 2);
+    type_length.Write(2, data_.len(), 2);
+    output->Splice(type_length, offset + 2);
+
+    // Insert the payload.
+    output->Splice(data_, offset + 6);
+
+    std::cerr << "Aft:" << *output << std::endl;
+    return true;
+  }
+
+ private:
+  uint16_t extension_;
+  DataBuffer data_;
+};
+
+class TlsExtensionTestBase : public TlsConnectTestBase {
+ protected:
+  TlsExtensionTestBase(Mode mode, uint16_t version)
+    : TlsConnectTestBase(mode, version) {}
+
+  void ClientHelloErrorTest(PacketFilter* filter,
+                            uint8_t alert = kTlsAlertDecodeError) {
+    auto alert_recorder = new TlsAlertRecorder();
+    server_->SetPacketFilter(alert_recorder);
+    if (filter) {
+      client_->SetPacketFilter(filter);
+    }
+    ConnectExpectFail();
+    ASSERT_EQ(kTlsAlertFatal, alert_recorder->level());
+    ASSERT_EQ(alert, alert_recorder->description());
+  }
+
+  void ServerHelloErrorTest(PacketFilter* filter,
+                            uint8_t alert = kTlsAlertDecodeError) {
+    auto alert_recorder = new TlsAlertRecorder();
+    client_->SetPacketFilter(alert_recorder);
+    if (filter) {
+      server_->SetPacketFilter(filter);
+    }
+    ConnectExpectFail();
+    ASSERT_EQ(kTlsAlertFatal, alert_recorder->level());
+    ASSERT_EQ(alert, alert_recorder->description());
+  }
+
+  static void InitSimpleSni(DataBuffer* extension) {
+    const char* name = "host.name";
+    const size_t namelen = PL_strlen(name);
+    extension->Allocate(namelen + 5);
+    extension->Write(0, namelen + 3, 2);
+    extension->Write(2, static_cast<uint32_t>(0), 1); // 0 == hostname
+    extension->Write(3, namelen, 2);
+    extension->Write(5, reinterpret_cast<const uint8_t*>(name), namelen);
+  }
+};
+
+class TlsExtensionTestDtls
+  : public TlsExtensionTestBase,
+    public ::testing::WithParamInterface<uint16_t> {
+ public:
+  TlsExtensionTestDtls() : TlsExtensionTestBase(DGRAM, GetParam()) {}
+};
+
+class TlsExtensionTest12Plus
+  : public TlsExtensionTestBase,
+    public ::testing::WithParamInterface<std::string> {
+ public:
+  TlsExtensionTest12Plus()
+    : TlsExtensionTestBase(TlsConnectTestBase::ToMode(GetParam()),
+                           SSL_LIBRARY_VERSION_TLS_1_2) {}
+};
+
+class TlsExtensionTestGeneric
+  : public TlsExtensionTestBase,
+    public ::testing::WithParamInterface<std::tuple<std::string, uint16_t>> {
+ public:
+  TlsExtensionTestGeneric()
+    : TlsExtensionTestBase(TlsConnectTestBase::ToMode((std::get<0>(GetParam()))),
+                           std::get<1>(GetParam())) {}
+};
+
+TEST_P(TlsExtensionTestGeneric, DamageSniLength) {
+  ClientHelloErrorTest(new TlsExtensionDamager(ssl_server_name_xtn, 1));
+}
+
+TEST_P(TlsExtensionTestGeneric, DamageSniHostLength) {
+  ClientHelloErrorTest(new TlsExtensionDamager(ssl_server_name_xtn, 4));
+}
+
+TEST_P(TlsExtensionTestGeneric, TruncateSni) {
+  ClientHelloErrorTest(new TlsExtensionTruncator(ssl_server_name_xtn, 7));
+}
+
+// A valid extension that appears twice will be reported as unsupported.
+TEST_P(TlsExtensionTestGeneric, RepeatSni) {
+  DataBuffer extension;
+  InitSimpleSni(&extension);
+  ClientHelloErrorTest(new TlsExtensionInjector(ssl_server_name_xtn, extension),
+                       kTlsAlertIllegalParameter);
+}
+
+// An SNI entry with zero length is considered invalid (strangely, not if it is
+// the last entry, which is probably a bug).
+TEST_P(TlsExtensionTestGeneric, BadSni) {
+  DataBuffer simple;
+  InitSimpleSni(&simple);
+  DataBuffer extension;
+  extension.Allocate(simple.len() + 3);
+  extension.Write(0, static_cast<uint32_t>(0), 3);
+  extension.Write(3, simple);
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_server_name_xtn, extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, EmptyAlpnExtension) {
+  EnableAlpn();
+  DataBuffer extension;
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension),
+                       kTlsAlertIllegalParameter);
+}
+
+// An empty ALPN isn't considered bad, though it does lead to there being no
+// protocol for the server to select.
+TEST_P(TlsExtensionTestGeneric, EmptyAlpnList) {
+  EnableAlpn();
+  const uint8_t val[] = { 0x00, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension),
+                       kTlsAlertNoApplicationProtocol);
+}
+
+TEST_P(TlsExtensionTestGeneric, OneByteAlpn) {
+  EnableAlpn();
+  ClientHelloErrorTest(new TlsExtensionTruncator(ssl_app_layer_protocol_xtn, 1));
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnMissingValue) {
+  EnableAlpn();
+  // This will leave the length of the second entry, but no value.
+  ClientHelloErrorTest(new TlsExtensionTruncator(ssl_app_layer_protocol_xtn, 5));
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnZeroLength) {
+  EnableAlpn();
+  const uint8_t val[] = { 0x01, 0x61, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnMismatch) {
+  const uint8_t client_alpn[] = { 0x01, 0x61 };
+  client_->EnableAlpn(client_alpn, sizeof(client_alpn));
+  const uint8_t server_alpn[] = { 0x02, 0x61, 0x62 };
+  server_->EnableAlpn(server_alpn, sizeof(server_alpn));
+
+  ClientHelloErrorTest(nullptr, kTlsAlertNoApplicationProtocol);
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnReturnedEmptyList) {
+  EnableAlpn();
+  const uint8_t val[] = { 0x00, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ServerHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnReturnedEmptyName) {
+  EnableAlpn();
+  const uint8_t val[] = { 0x00, 0x01, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ServerHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnReturnedListTrailingData) {
+  EnableAlpn();
+  const uint8_t val[] = { 0x00, 0x02, 0x01, 0x61, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ServerHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnReturnedExtraEntry) {
+  EnableAlpn();
+  const uint8_t val[] = { 0x00, 0x04, 0x01, 0x61, 0x01, 0x62 };
+  DataBuffer extension(val, sizeof(val));
+  ServerHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnReturnedBadListLength) {
+  EnableAlpn();
+  const uint8_t val[] = { 0x00, 0x99, 0x01, 0x61, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ServerHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, AlpnReturnedBadNameLength) {
+  EnableAlpn();
+  const uint8_t val[] = { 0x00, 0x02, 0x99, 0x61 };
+  DataBuffer extension(val, sizeof(val));
+  ServerHelloErrorTest(new TlsExtensionReplacer(ssl_app_layer_protocol_xtn, extension));
+}
+
+TEST_P(TlsExtensionTestDtls, SrtpShort) {
+  EnableSrtp();
+  ClientHelloErrorTest(new TlsExtensionTruncator(ssl_use_srtp_xtn, 3));
+}
+
+TEST_P(TlsExtensionTestDtls, SrtpOdd) {
+  EnableSrtp();
+  const uint8_t val[] = { 0x00, 0x01, 0xff, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_use_srtp_xtn, extension));
+}
+
+TEST_P(TlsExtensionTest12Plus, SignatureAlgorithmsBadLength) {
+  const uint8_t val[] = { 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_signature_algorithms_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTest12Plus, SignatureAlgorithmsTrailingData) {
+  const uint8_t val[] = { 0x00, 0x02, 0x04, 0x01, 0x00 }; // sha-256, rsa
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_signature_algorithms_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTest12Plus, SignatureAlgorithmsEmpty) {
+  const uint8_t val[] = { 0x00, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_signature_algorithms_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTest12Plus, SignatureAlgorithmsOddLength) {
+  const uint8_t val[] = { 0x00, 0x01, 0x04 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_signature_algorithms_xtn,
+                                                extension));
+}
+
+// The extension handling ignores unsupported hashes, so breaking that has no
+// effect on success rates.  However, ssl3_SendServerKeyExchange catches an
+// unsupported signature algorithm.
+
+// This actually fails with a decryption error (fatal alert 51).  That's a bad
+// to fail, since any tampering with the handshake will trigger that alert when
+// verifying the Finished message.  Thus, this test is disabled until this error
+// is turned into an alert.
+TEST_P(TlsExtensionTest12Plus, DISABLED_SignatureAlgorithmsSigUnsupported) {
+  const uint8_t val[] = { 0x00, 0x02, 0x04, 0x99 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_signature_algorithms_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, SupportedCurvesShort) {
+  EnableSomeECDHECiphers();
+  const uint8_t val[] = { 0x00, 0x01, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, SupportedCurvesBadLength) {
+  EnableSomeECDHECiphers();
+  const uint8_t val[] = { 0x09, 0x99, 0x00, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, SupportedCurvesTrailingData) {
+  EnableSomeECDHECiphers();
+  const uint8_t val[] = { 0x00, 0x02, 0x00, 0x00, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, SupportedPointsEmpty) {
+  EnableSomeECDHECiphers();
+  const uint8_t val[] = { 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, SupportedPointsBadLength) {
+  EnableSomeECDHECiphers();
+  const uint8_t val[] = { 0x99, 0x00, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, SupportedPointsTrailingData) {
+  EnableSomeECDHECiphers();
+  const uint8_t val[] = { 0x01, 0x00, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, RenegotiationInfoBadLength) {
+  const uint8_t val[] = { 0x99 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_renegotiation_info_xtn,
+                                                extension));
+}
+
+TEST_P(TlsExtensionTestGeneric, RenegotiationInfoMismatch) {
+  const uint8_t val[] = { 0x01, 0x00 };
+  DataBuffer extension(val, sizeof(val));
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_renegotiation_info_xtn,
+                                                extension));
+}
+
+// The extension has to contain a length.
+TEST_P(TlsExtensionTestGeneric, RenegotiationInfoExtensionEmpty) {
+  DataBuffer extension;
+  ClientHelloErrorTest(new TlsExtensionReplacer(ssl_renegotiation_info_xtn,
+                                                extension));
+}
+
+INSTANTIATE_TEST_CASE_P(ExtensionTls10, TlsExtensionTestGeneric,
+                        ::testing::Combine(
+                          TlsConnectTestBase::kTlsModesStream,
+                          TlsConnectTestBase::kTlsV10));
+INSTANTIATE_TEST_CASE_P(ExtensionVariants, TlsExtensionTestGeneric,
+                        ::testing::Combine(
+                          TlsConnectTestBase::kTlsModesAll,
+                          TlsConnectTestBase::kTlsV11V12));
+INSTANTIATE_TEST_CASE_P(ExtensionTls12Plus, TlsExtensionTest12Plus,
+                        TlsConnectTestBase::kTlsModesAll);
+INSTANTIATE_TEST_CASE_P(ExtensionDgram, TlsExtensionTestDtls,
+                        TlsConnectTestBase::kTlsV11V12);
+
+}  // namespace nspr_test
--- a/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
@@ -39,23 +39,17 @@ class TlsServerKeyExchangeECDHE {
 
   DataBuffer public_key_;
 };
 
 TEST_P(TlsConnectGeneric, SetupOnly) {}
 
 TEST_P(TlsConnectGeneric, Connect) {
   Connect();
-
-  // Check that we negotiated the expected version.
-  if (mode_ == STREAM) {
-    client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_0);
-  } else {
-    client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_1);
-  }
+  client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_2);
 }
 
 TEST_P(TlsConnectGeneric, ConnectResumed) {
   ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
   Connect();
 
   Reset();
   Connect();
@@ -147,59 +141,36 @@ TEST_P(TlsConnectGeneric, ConnectClientN
   Connect();
 
   Reset();
   ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
   Connect();
   CheckResumption(RESUME_NONE);
 }
 
-TEST_P(TlsConnectGeneric, ConnectTLS_1_1_Only) {
-  EnsureTlsSetup();
-  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
-                           SSL_LIBRARY_VERSION_TLS_1_1);
-
-  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
-                           SSL_LIBRARY_VERSION_TLS_1_1);
-
-  Connect();
-
-  client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_1);
-}
-
-TEST_P(TlsConnectGeneric, ConnectTLS_1_2_Only) {
-  EnsureTlsSetup();
-  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
-                           SSL_LIBRARY_VERSION_TLS_1_2);
-  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
-                           SSL_LIBRARY_VERSION_TLS_1_2);
-  Connect();
-  client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_2);
-}
-
 TEST_P(TlsConnectGeneric, ConnectAlpn) {
   EnableAlpn();
   Connect();
   client_->CheckAlpn(SSL_NEXT_PROTO_SELECTED, "a");
   server_->CheckAlpn(SSL_NEXT_PROTO_NEGOTIATED, "a");
 }
 
-TEST_F(DtlsConnectTest, ConnectSrtp) {
+TEST_P(TlsConnectDatagram, ConnectSrtp) {
   EnableSrtp();
   Connect();
   CheckSrtp();
 }
 
-TEST_F(TlsConnectTest, ConnectECDHE) {
+TEST_P(TlsConnectStream, ConnectECDHE) {
   EnableSomeECDHECiphers();
   Connect();
   client_->CheckKEAType(ssl_kea_ecdh);
 }
 
-TEST_F(TlsConnectTest, ConnectECDHETwiceReuseKey) {
+TEST_P(TlsConnectStream, ConnectECDHETwiceReuseKey) {
   EnableSomeECDHECiphers();
   TlsInspectorRecordHandshakeMessage* i1 =
       new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
   server_->SetPacketFilter(i1);
   Connect();
   client_->CheckKEAType(ssl_kea_ecdh);
   TlsServerKeyExchangeECDHE dhe1;
   ASSERT_TRUE(dhe1.Parse(i1->buffer()));
@@ -218,17 +189,17 @@ TEST_F(TlsConnectTest, ConnectECDHETwice
   ASSERT_TRUE(dhe2.Parse(i2->buffer()));
 
   // Make sure they are the same.
   ASSERT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len());
   ASSERT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
                       dhe1.public_key_.len()));
 }
 
-TEST_F(TlsConnectTest, ConnectECDHETwiceNewKey) {
+TEST_P(TlsConnectStream, ConnectECDHETwiceNewKey) {
   EnableSomeECDHECiphers();
   SECStatus rv =
       SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
   ASSERT_EQ(SECSuccess, rv);
   TlsInspectorRecordHandshakeMessage* i1 =
       new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
   server_->SetPacketFilter(i1);
   Connect();
@@ -252,12 +223,22 @@ TEST_F(TlsConnectTest, ConnectECDHETwice
   ASSERT_TRUE(dhe2.Parse(i2->buffer()));
 
   // Make sure they are different.
   ASSERT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) &&
                (!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
                         dhe1.public_key_.len())));
 }
 
-INSTANTIATE_TEST_CASE_P(Variants, TlsConnectGeneric,
-                        ::testing::Values("TLS", "DTLS"));
+INSTANTIATE_TEST_CASE_P(VariantsStream10, TlsConnectGeneric,
+                        ::testing::Combine(
+                          TlsConnectTestBase::kTlsModesStream,
+                          TlsConnectTestBase::kTlsV10));
+INSTANTIATE_TEST_CASE_P(VariantsAll, TlsConnectGeneric,
+                        ::testing::Combine(
+                          TlsConnectTestBase::kTlsModesAll,
+                          TlsConnectTestBase::kTlsV11V12));
+INSTANTIATE_TEST_CASE_P(VersionsDatagram, TlsConnectDatagram,
+                        TlsConnectTestBase::kTlsV11V12);
+INSTANTIATE_TEST_CASE_P(VersionsDatagram, TlsConnectStream,
+                        TlsConnectTestBase::kTlsV11V12);
 
 }  // namespace nspr_test
--- a/security/nss/external_tests/ssl_gtest/test_io.cc
+++ b/security/nss/external_tests/ssl_gtest/test_io.cc
@@ -323,59 +323,49 @@ int32_t DummyPrSocket::Read(void *data, 
 
 int32_t DummyPrSocket::Recv(void *buf, int32_t buflen) {
   if (input_.empty()) {
     PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
     return -1;
   }
 
   Packet *front = input_.front();
-  if (buflen < front->len()) {
+  if (static_cast<size_t>(buflen) < front->len()) {
     PR_ASSERT(false);
     PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
     return -1;
   }
 
   size_t count = front->len();
   memcpy(buf, front->data(), count);
 
   input_.pop();
   delete front;
 
   return static_cast<int32_t>(count);
 }
 
 int32_t DummyPrSocket::Write(const void *buf, int32_t length) {
-  DataBuffer packet(static_cast<const uint8_t*>(buf),
-                    static_cast<size_t>(length));
-  if (filter_) {
-    DataBuffer filtered;
-    if (filter_->Filter(packet, &filtered)) {
-      if (WriteDirect(filtered) != filtered.len()) {
-        PR_SetError(PR_IO_ERROR, 0);
-        return -1;
-      }
-      LOG("Wrote: " << packet);
-      // libssl can't handle if this reports something other than the length of
-      // what was passed in (or less, but we're not doing partial writes).
-      return packet.len();
-    }
-  }
-
-  return WriteDirect(packet);
-}
-
-int32_t DummyPrSocket::WriteDirect(const DataBuffer& packet) {
   if (!peer_) {
     PR_SetError(PR_IO_ERROR, 0);
     return -1;
   }
 
-  peer_->PacketReceived(packet);
-  return static_cast<int32_t>(packet.len()); // ignore truncation
+  DataBuffer packet(static_cast<const uint8_t*>(buf),
+                    static_cast<size_t>(length));
+  DataBuffer filtered;
+  if (filter_ && filter_->Filter(packet, &filtered)) {
+    LOG("Filtered packet: " << filtered);
+    peer_->PacketReceived(filtered);
+  } else {
+    peer_->PacketReceived(packet);
+  }
+  // libssl can't handle it if this reports something other than the length
+  // of what was passed in (or less, but we're not doing partial writes).
+  return static_cast<int32_t>(packet.len());
 }
 
 Poller *Poller::instance;
 
 Poller *Poller::Instance() {
   if (!instance) instance = new Poller();
 
   return instance;
--- a/security/nss/external_tests/ssl_gtest/test_io.h
+++ b/security/nss/external_tests/ssl_gtest/test_io.h
@@ -7,16 +7,17 @@
 #ifndef test_io_h_
 #define test_io_h_
 
 #include <string.h>
 #include <map>
 #include <memory>
 #include <queue>
 #include <string>
+#include <ostream>
 
 #include "prio.h"
 
 namespace nss_test {
 
 class DataBuffer;
 class Packet;
 class DummyPrSocket;  // Fwd decl.
@@ -31,33 +32,36 @@ class PacketFilter {
   // A filter that modifies the data places the modified data in *output and
   // returns true.  A filter that does not modify data returns false, in which
   // case the value in *output is ignored.
   virtual bool Filter(const DataBuffer& input, DataBuffer* output) = 0;
 };
 
 enum Mode { STREAM, DGRAM };
 
+inline std::ostream& operator<<(std::ostream& os, Mode m) {
+  return os << ((m == STREAM) ? "TLS" : "DTLS");
+}
+
 class DummyPrSocket {
  public:
   ~DummyPrSocket();
 
   static PRFileDesc* CreateFD(const std::string& name,
                               Mode mode);  // Returns an FD.
   static DummyPrSocket* GetAdapter(PRFileDesc* fd);
 
   void SetPeer(DummyPrSocket* peer) { peer_ = peer; }
 
   void SetPacketFilter(PacketFilter* filter) { filter_ = filter; }
 
   void PacketReceived(const DataBuffer& data);
   int32_t Read(void* data, int32_t len);
   int32_t Recv(void* buf, int32_t buflen);
   int32_t Write(const void* buf, int32_t length);
-  int32_t WriteDirect(const DataBuffer& data);
 
   Mode mode() const { return mode_; }
   bool readable() const { return !input_.empty(); }
   bool writable() { return true; }
 
  private:
   DummyPrSocket(const std::string& name, Mode mode)
       : name_(name),
--- a/security/nss/external_tests/ssl_gtest/tls_agent.cc
+++ b/security/nss/external_tests/ssl_gtest/tls_agent.cc
@@ -53,18 +53,22 @@ bool TlsAgent::EnsureTlsSetup() {
                                  reinterpret_cast<void*>(this));
     EXPECT_EQ(SECSuccess, rv);  // don't abort, just fail
   } else {
     SECStatus rv = SSL_SetURL(ssl_fd_, "server");
     EXPECT_EQ(SECSuccess, rv);
     if (rv != SECSuccess) return false;
   }
 
-  SECStatus rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook,
-                                         reinterpret_cast<void*>(this));
+  SECStatus rv = SSL_VersionRangeSet(ssl_fd_, &vrange_);
+  EXPECT_EQ(SECSuccess, rv);
+  if (rv != SECSuccess) return false;
+
+  rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook,
+                               reinterpret_cast<void*>(this));
   EXPECT_EQ(SECSuccess, rv);
   if (rv != SECSuccess) return false;
 
   return true;
 }
 
 void TlsAgent::StartConnect() {
   ASSERT_TRUE(EnsureTlsSetup());
@@ -99,18 +103,23 @@ void TlsAgent::SetSessionCacheEnabled(bo
   ASSERT_TRUE(EnsureTlsSetup());
 
   SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_NO_CACHE,
                                en ? PR_FALSE : PR_TRUE);
   ASSERT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) {
-  SSLVersionRange range = {minver, maxver};
-  ASSERT_EQ(SECSuccess, SSL_VersionRangeSet(ssl_fd_, &range));
+   vrange_.min = minver;
+   vrange_.max = maxver;
+
+   if (ssl_fd_) {
+     SECStatus rv = SSL_VersionRangeSet(ssl_fd_, &vrange_);
+     ASSERT_EQ(SECSuccess, rv);
+   }
 }
 
 void TlsAgent::CheckKEAType(SSLKEAType type) const {
   ASSERT_EQ(CONNECTED, state_);
   ASSERT_EQ(type, csinfo_.keaType);
 }
 
 void TlsAgent::CheckVersion(uint16_t version) const {
--- a/security/nss/external_tests/ssl_gtest/tls_agent.h
+++ b/security/nss/external_tests/ssl_gtest/tls_agent.h
@@ -9,16 +9,19 @@
 
 #include "prio.h"
 #include "ssl.h"
 
 #include <iostream>
 
 #include "test_io.h"
 
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+
 namespace nss_test {
 
 #define LOG(msg) std::cerr << name_ << ": " << msg << std::endl
 
 enum SessionResumptionMode {
   RESUME_NONE = 0,
   RESUME_SESSIONID = 1,
   RESUME_TICKET = 2,
@@ -35,16 +38,20 @@ class TlsAgent : public PollTarget {
         mode_(mode),
         pr_fd_(nullptr),
         adapter_(nullptr),
         ssl_fd_(nullptr),
         role_(role),
         state_(INIT) {
       memset(&info_, 0, sizeof(info_));
       memset(&csinfo_, 0, sizeof(csinfo_));
+      SECStatus rv = SSL_VersionRangeGetDefault(mode_ == STREAM ?
+                                                ssl_variant_stream : ssl_variant_datagram,
+                                                &vrange_);
+      EXPECT_EQ(SECSuccess, rv);
   }
 
   ~TlsAgent() {
     if (pr_fd_) {
       PR_Close(pr_fd_);
     }
 
     if (ssl_fd_) {
@@ -90,24 +97,33 @@ class TlsAgent : public PollTarget {
   State state() const { return state_; }
 
   const char* state_str() const { return state_str(state()); }
 
   const char* state_str(State state) const { return states[state]; }
 
   PRFileDesc* ssl_fd() { return ssl_fd_; }
 
+  uint16_t min_version() const { return vrange_.min; }
+  uint16_t max_version() const { return vrange_.max; }
+
   bool version(uint16_t* version) const {
     if (state_ != CONNECTED) return false;
 
     *version = info_.protocolVersion;
 
     return true;
   }
 
+  uint16_t version() const {
+    EXPECT_EQ(CONNECTED, state_);
+
+    return info_.protocolVersion;
+  }
+
   bool cipher_suite(int16_t* cipher_suite) const {
     if (state_ != CONNECTED) return false;
 
     *cipher_suite = info_.cipherSuite;
     return true;
   }
 
   std::string cipher_suite_name() const {
@@ -158,13 +174,14 @@ class TlsAgent : public PollTarget {
   Mode mode_;
   PRFileDesc* pr_fd_;
   DummyPrSocket* adapter_;
   PRFileDesc* ssl_fd_;
   Role role_;
   State state_;
   SSLChannelInfo info_;
   SSLCipherSuiteInfo csinfo_;
+  SSLVersionRange vrange_;
 };
 
 }  // namespace nss_test
 
 #endif
--- a/security/nss/external_tests/ssl_gtest/tls_connect.cc
+++ b/security/nss/external_tests/ssl_gtest/tls_connect.cc
@@ -3,26 +3,66 @@
 /* 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 "tls_connect.h"
 
 #include <iostream>
 
+#include "sslproto.h"
 #include "gtest_utils.h"
 
 extern std::string g_working_dir_path;
 
 namespace nss_test {
 
-TlsConnectTestBase::TlsConnectTestBase(Mode mode)
+static const std::string kTlsModesStreamArr[] = {"TLS"};
+::testing::internal::ParamGenerator<std::string>
+  TlsConnectTestBase::kTlsModesStream = ::testing::ValuesIn(kTlsModesStreamArr);
+static const std::string kTlsModesAllArr[] = {"TLS", "DTLS"};
+::testing::internal::ParamGenerator<std::string>
+  TlsConnectTestBase::kTlsModesAll = ::testing::ValuesIn(kTlsModesAllArr);
+static const uint16_t kTlsV10Arr[] = {SSL_LIBRARY_VERSION_TLS_1_0};
+::testing::internal::ParamGenerator<uint16_t>
+  TlsConnectTestBase::kTlsV10 = ::testing::ValuesIn(kTlsV10Arr);
+static const uint16_t kTlsV11V12Arr[] = {SSL_LIBRARY_VERSION_TLS_1_1,
+                                         SSL_LIBRARY_VERSION_TLS_1_2};
+::testing::internal::ParamGenerator<uint16_t>
+  TlsConnectTestBase::kTlsV11V12 = ::testing::ValuesIn(kTlsV11V12Arr);
+// TODO: add TLS 1.3
+static const uint16_t kTlsV12PlusArr[] = {SSL_LIBRARY_VERSION_TLS_1_2};
+::testing::internal::ParamGenerator<uint16_t>
+  TlsConnectTestBase::kTlsV12Plus = ::testing::ValuesIn(kTlsV12PlusArr);
+
+static std::string VersionString(uint16_t version) {
+  switch(version) {
+  case 0:
+    return "(no version)";
+  case SSL_LIBRARY_VERSION_TLS_1_0:
+    return "1.0";
+  case SSL_LIBRARY_VERSION_TLS_1_1:
+    return "1.1";
+  case SSL_LIBRARY_VERSION_TLS_1_2:
+    return "1.2";
+  default:
+    std::cerr << "Invalid version: " << version << std::endl;
+    EXPECT_TRUE(false);
+    return "";
+  }
+}
+
+TlsConnectTestBase::TlsConnectTestBase(Mode mode, uint16_t version)
       : mode_(mode),
         client_(new TlsAgent("client", TlsAgent::CLIENT, mode_)),
-        server_(new TlsAgent("server", TlsAgent::SERVER, mode_)) {}
+        server_(new TlsAgent("server", TlsAgent::SERVER, mode_)),
+        version_(version),
+        session_ids_() {
+  std::cerr << "Version: " << mode_ << " " << VersionString(version_) << std::endl;
+}
 
 TlsConnectTestBase::~TlsConnectTestBase() {
   delete client_;
   delete server_;
 }
 
 void TlsConnectTestBase::SetUp() {
   // Configure a fresh session cache.
@@ -44,16 +84,21 @@ void TlsConnectTestBase::TearDown() {
 }
 
 void TlsConnectTestBase::Init() {
   ASSERT_TRUE(client_->Init());
   ASSERT_TRUE(server_->Init());
 
   client_->SetPeer(server_);
   server_->SetPeer(client_);
+
+  if (version_) {
+    client_->SetVersionRange(version_, version_);
+    server_->SetVersionRange(version_, version_);
+  }
 }
 
 void TlsConnectTestBase::Reset() {
   delete client_;
   delete server_;
 
   client_ = new TlsAgent("client", TlsAgent::CLIENT, mode_);
   server_ = new TlsAgent("server", TlsAgent::SERVER, mode_);
@@ -67,42 +112,50 @@ void TlsConnectTestBase::EnsureTlsSetup(
 }
 
 void TlsConnectTestBase::Handshake() {
   server_->StartConnect();
   client_->StartConnect();
   client_->Handshake();
   server_->Handshake();
 
-  ASSERT_TRUE_WAIT(client_->state() != TlsAgent::CONNECTING &&
-                   server_->state() != TlsAgent::CONNECTING,
+  ASSERT_TRUE_WAIT((client_->state() != TlsAgent::CONNECTING) &&
+                   (server_->state() != TlsAgent::CONNECTING),
                    5000);
+
 }
 
 void TlsConnectTestBase::Connect() {
   Handshake();
 
+  // Check the version is as expected
+  ASSERT_EQ(client_->version(), server_->version());
+  ASSERT_EQ(std::min(client_->max_version(),
+                     server_->max_version()),
+            client_->version());
+
   ASSERT_EQ(TlsAgent::CONNECTED, client_->state());
   ASSERT_EQ(TlsAgent::CONNECTED, server_->state());
 
   int16_t cipher_suite1, cipher_suite2;
   bool ret = client_->cipher_suite(&cipher_suite1);
   ASSERT_TRUE(ret);
   ret = server_->cipher_suite(&cipher_suite2);
   ASSERT_TRUE(ret);
   ASSERT_EQ(cipher_suite1, cipher_suite2);
 
-  std::cerr << "Connected with cipher suite " << client_->cipher_suite_name()
+  std::cerr << "Connected with version " << client_->version()
+            << " cipher suite " << client_->cipher_suite_name()
             << std::endl;
 
   // Check and store session ids.
   std::vector<uint8_t> sid_c1 = client_->session_id();
-  ASSERT_EQ(32, sid_c1.size());
+  ASSERT_EQ(32U, sid_c1.size());
   std::vector<uint8_t> sid_s1 = server_->session_id();
-  ASSERT_EQ(32, sid_s1.size());
+  ASSERT_EQ(32U, sid_s1.size());
   ASSERT_EQ(sid_c1, sid_s1);
   session_ids_.push_back(sid_c1);
 }
 
 void TlsConnectTestBase::ConnectExpectFail() {
   Handshake();
 
   ASSERT_EQ(TlsAgent::ERROR, client_->state());
@@ -131,17 +184,17 @@ void TlsConnectTestBase::CheckResumption
   ASSERT_EQ(resume_ct, stats->hch_sid_cache_hits);
   ASSERT_EQ(resume_ct, stats->hsh_sid_cache_hits);
 
   ASSERT_EQ(stateless_ct, stats->hch_sid_stateless_resumes);
   ASSERT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes);
 
   if (resume_ct) {
     // Check that the last two session ids match.
-    ASSERT_GE(2, session_ids_.size());
+    ASSERT_GE(2U, session_ids_.size());
     ASSERT_EQ(session_ids_[session_ids_.size()-1],
               session_ids_[session_ids_.size()-2]);
   }
 }
 
 void TlsConnectTestBase::EnableAlpn() {
   // A simple value of "a", "b".  Note that the preferred value of "a" is placed
   // at the end, because the NSS API follows the now defunct NPN specification,
@@ -158,13 +211,12 @@ void TlsConnectTestBase::EnableSrtp() {
 }
 
 void TlsConnectTestBase::CheckSrtp() {
   client_->CheckSrtp();
   server_->CheckSrtp();
 }
 
 TlsConnectGeneric::TlsConnectGeneric()
-    : TlsConnectTestBase((GetParam() == "TLS") ? STREAM : DGRAM) {
-  std::cerr << "Variant: " << GetParam() << std::endl;
-}
+  : TlsConnectTestBase(TlsConnectTestBase::ToMode(std::get<0>(GetParam())),
+                       std::get<1>(GetParam())) {}
 
 } // namespace nss_test
--- a/security/nss/external_tests/ssl_gtest/tls_connect.h
+++ b/security/nss/external_tests/ssl_gtest/tls_connect.h
@@ -2,29 +2,41 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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_connect_h_
 #define tls_connect_h_
 
+#include <tuple>
+
 #include "sslt.h"
 
 #include "tls_agent.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 
 namespace nss_test {
 
 // A generic TLS connection test base.
 class TlsConnectTestBase : public ::testing::Test {
  public:
-  TlsConnectTestBase(Mode mode);
+  static ::testing::internal::ParamGenerator<std::string> kTlsModesStream;
+  static ::testing::internal::ParamGenerator<std::string> kTlsModesAll;
+  static ::testing::internal::ParamGenerator<uint16_t> kTlsV10;
+  static ::testing::internal::ParamGenerator<uint16_t> kTlsV11V12;
+  static ::testing::internal::ParamGenerator<uint16_t> kTlsV12Plus;
+
+  static inline Mode ToMode(const std::string& str) {
+    return str == "TLS" ? STREAM : DGRAM;
+  }
+
+  TlsConnectTestBase(Mode mode, uint16_t version);
   virtual ~TlsConnectTestBase();
 
   void SetUp();
   void TearDown();
 
   // Initialize client and server.
   void Init();
   // Re-initialize client and server.
@@ -41,39 +53,44 @@ class TlsConnectTestBase : public ::test
 
   void EnableSomeECDHECiphers();
   void ConfigureSessionCache(SessionResumptionMode client,
                              SessionResumptionMode server);
   void CheckResumption(SessionResumptionMode expected);
   void EnableAlpn();
   void EnableSrtp();
   void CheckSrtp();
+ protected:
 
- protected:
   Mode mode_;
   TlsAgent* client_;
   TlsAgent* server_;
+  uint16_t version_;
   std::vector<std::vector<uint8_t>> session_ids_;
 };
 
 // A TLS-only test base.
-class TlsConnectTest : public TlsConnectTestBase {
+class TlsConnectStream : public TlsConnectTestBase,
+                         public ::testing::WithParamInterface<uint16_t> {
  public:
-  TlsConnectTest() : TlsConnectTestBase(STREAM) {}
+  TlsConnectStream() : TlsConnectTestBase(STREAM, GetParam()) {}
 };
 
 // A DTLS-only test base.
-class DtlsConnectTest : public TlsConnectTestBase {
+class TlsConnectDatagram : public TlsConnectTestBase,
+                           public ::testing::WithParamInterface<uint16_t> {
  public:
-  DtlsConnectTest() : TlsConnectTestBase(DGRAM) {}
+  TlsConnectDatagram() : TlsConnectTestBase(DGRAM, GetParam()) {}
 };
 
-// A generic test class that can be either STREAM or DGRAM.  This is configured
-// in ssl_loopback_unittest.cc.  All uses of this should use TEST_P().
-class TlsConnectGeneric : public TlsConnectTestBase,
-                          public ::testing::WithParamInterface<std::string> {
+// A generic test class that can be either STREAM or DGRAM and a single version
+// of TLS.  This is configured in ssl_loopback_unittest.cc.  All uses of this
+// should use TEST_P().
+class TlsConnectGeneric
+  : public TlsConnectTestBase,
+    public ::testing::WithParamInterface<std::tuple<std::string, uint16_t>> {
  public:
   TlsConnectGeneric();
 };
 
 } // namespace nss_test
 
 #endif
--- a/security/nss/external_tests/ssl_gtest/tls_filter.cc
+++ b/security/nss/external_tests/ssl_gtest/tls_filter.cc
@@ -187,16 +187,18 @@ bool TlsAlertRecorder::FilterRecord(uint
                                     const DataBuffer& input, DataBuffer* output) {
   if (level_ == kTlsAlertFatal) { // already fatal
     return false;
   }
   if (content_type != kTlsAlertType) {
     return false;
   }
 
+  std::cerr << "Alert: " << input << std::endl;
+
   TlsParser parser(input);
   uint8_t lvl;
   if (!parser.Read(&lvl)) {
     return false;
   }
   if (lvl == kTlsAlertWarning) { // not strong enough
     return false;
   }
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -28,22 +28,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION  "3.18" _NSS_ECC_STRING _NSS_CUSTOMIZED
+#define NSS_VERSION  "3.18.1" _NSS_ECC_STRING _NSS_CUSTOMIZED " Beta"
 #define NSS_VMAJOR   3
 #define NSS_VMINOR   18
-#define NSS_VPATCH   0
-#define NSS_VBUILD   2
-#define NSS_BETA     PR_FALSE
+#define NSS_VPATCH   1
+#define NSS_VBUILD   0
+#define NSS_BETA     PR_TRUE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/softoken/config.mk
+++ b/security/nss/lib/softoken/config.mk
@@ -17,42 +17,42 @@ ifeq (,$(filter-out WIN%,$(OS_TARGET)))
 SHARED_LIBRARY = $(OBJDIR)/$(DLL_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX)
 IMPORT_LIBRARY = $(OBJDIR)/$(IMPORT_LIB_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION)$(IMPORT_LIB_SUFFIX)
 
 RES = $(OBJDIR)/$(LIBRARY_NAME).res
 RESNAME = $(LIBRARY_NAME).rc
 
 ifdef NS_USE_GCC
 EXTRA_SHARED_LIBS += \
-	-L$(DIST)/lib \
+	-L$(SQLITE_LIB_DIR) \
 	-l$(SQLITE_LIB_NAME) \
 	-L$(NSSUTIL_LIB_DIR) \
 	-lnssutil3 \
 	-L$(NSPR_LIB_DIR) \
 	-lplc4 \
 	-lplds4 \
 	-lnspr4 \
 	$(NULL)
 else # ! NS_USE_GCC
 
 EXTRA_SHARED_LIBS += \
-	$(DIST)/lib/$(SQLITE_LIB_NAME).lib \
+	$(SQLITE_LIB_DIR)/$(SQLITE_LIB_NAME).lib \
 	$(NSSUTIL_LIB_DIR)/nssutil3.lib \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.lib \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.lib \
 	$(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)nspr4.lib \
 	$(NULL)
 endif # NS_USE_GCC
 
 else
 
 # $(PROGRAM) has NO explicit dependencies on $(EXTRA_SHARED_LIBS)
 # $(EXTRA_SHARED_LIBS) come before $(OS_LIBS), except on AIX.
 EXTRA_SHARED_LIBS += \
-	-L$(DIST)/lib \
+	-L$(SQLITE_LIB_DIR) \
 	-l$(SQLITE_LIB_NAME) \
 	-L$(NSSUTIL_LIB_DIR) \
 	-lnssutil3 \
 	-L$(NSPR_LIB_DIR) \
 	-lplc4 \
 	-lplds4 \
 	-lnspr4 \
 	$(NULL)
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -20,16 +20,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION  "3.18" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VERSION  "3.18.1" SOFTOKEN_ECC_STRING " Beta"
 #define SOFTOKEN_VMAJOR   3
 #define SOFTOKEN_VMINOR   18
-#define SOFTOKEN_VPATCH   0
-#define SOFTOKEN_VBUILD   2
-#define SOFTOKEN_BETA     PR_FALSE
+#define SOFTOKEN_VPATCH   1
+#define SOFTOKEN_VBUILD   0
+#define SOFTOKEN_BETA     PR_TRUE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -2783,16 +2783,22 @@ ssl3_SendRecord(   sslSocket *        ss
 
     SSL_TRC(3, ("%d: SSL3[%d] SendRecord type: %s nIn=%d",
 		SSL_GETPID(), ss->fd, ssl3_DecodeContentType(type),
 		nIn));
     PRINT_BUF(50, (ss, "Send record (plain text)", pIn, nIn));
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
 
+    if (ss->ssl3.fatalAlertSent) {
+        SSL_TRC(3, ("%d: SSL3[%d] Suppress write, fatal alert already sent",
+                    SSL_GETPID(), ss->fd));
+        return SECFailure;
+    }
+
     capRecordVersion = ((flags & ssl_SEND_FLAG_CAP_RECORD_VERSION) != 0);
 
     if (capRecordVersion) {
 	/* ssl_SEND_FLAG_CAP_RECORD_VERSION can only be used with the
 	 * TLS initial ClientHello. */
 	PORT_Assert(!IS_DTLS(ss));
 	PORT_Assert(!ss->firstHsDone);
 	PORT_Assert(type == content_handshake);
@@ -3228,16 +3234,19 @@ SSL3_SendAlert(sslSocket *ss, SSL3AlertL
     rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
     if (rv == SECSuccess) {
 	PRInt32 sent;
 	sent = ssl3_SendRecord(ss, 0, content_alert, bytes, 2, 
 			       desc == no_certificate 
 			       ? ssl_SEND_FLAG_FORCE_INTO_BUFFER : 0);
 	rv = (sent >= 0) ? SECSuccess : (SECStatus)sent;
     }
+    if (level == alert_fatal) {
+        ss->ssl3.fatalAlertSent = PR_TRUE;
+    }
     ssl_ReleaseXmitBufLock(ss);
     ssl_ReleaseSSL3HandshakeLock(ss);
     return rv;	/* error set by ssl3_FlushHandshake or ssl3_SendRecord */
 }
 
 /*
  * Send illegal_parameter alert.  Set generic error number.
  */
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -1,8 +1,9 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
  * SSL3 Protocol
  *
  * 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/. */
 
 /* ECC code moved here from ssl3con.c */
@@ -1179,32 +1180,31 @@ ssl3_SendSupportedPointFormatsXtn(
 SECStatus
 ssl3_HandleSupportedPointFormatsXtn(sslSocket *ss, PRUint16 ex_type,
                                     SECItem *data)
 {
     int i;
 
     if (data->len < 2 || data->len > 255 || !data->data ||
         data->len != (unsigned int)data->data[0] + 1) {
-        /* malformed */
-        goto loser;
+        return ssl3_DecodeError(ss);
     }
     for (i = data->len; --i > 0; ) {
         if (data->data[i] == 0) {
             /* indicate that we should send a reply */
             SECStatus rv;
             rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type,
                               &ssl3_SendSupportedPointFormatsXtn);
             return rv;
         }
     }
-loser:
+
     /* evil client doesn't support uncompressed */
     ssl3_DisableECCSuites(ss, ecSuites);
-    return SECFailure;
+    return SECSuccess;
 }
 
 
 #define SSL3_GET_SERVER_PUBLICKEY(sock, type) \
     (ss->serverCerts[type].serverKeyPair ? \
     ss->serverCerts[type].serverKeyPair->pubKey : NULL)
 
 /* Extract the TLS curve name for the public key in our EC server cert. */
@@ -1215,63 +1215,66 @@ ECName ssl3_GetSvrCertCurveName(sslSocke
 
     srvPublicKey = SSL3_GET_SERVER_PUBLICKEY(ss, kt_ecdh);
     if (srvPublicKey) {
         ec_curve = params2ecName(&srvPublicKey->u.ec.DEREncodedParams);
     }
     return ec_curve;
 }
 
-/* Ensure that the curve in our server cert is one of the ones suppored
+/* Ensure that the curve in our server cert is one of the ones supported
  * by the remote client, and disable all ECC cipher suites if not.
  */
 SECStatus
 ssl3_HandleSupportedCurvesXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
 {
     PRInt32  list_len;
     PRUint32 peerCurves   = 0;
     PRUint32 mutualCurves = 0;
     PRUint16 svrCertCurveName;
 
-    if (!data->data || data->len < 4 || data->len > 65535)
-        goto loser;
+    if (!data->data || data->len < 4) {
+        (void)ssl3_DecodeError(ss);
+        return SECFailure;
+    }
+
     /* get the length of elliptic_curve_list */
     list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
     if (list_len < 0 || data->len != list_len || (data->len % 2) != 0) {
-        /* malformed */
-        goto loser;
+        (void)ssl3_DecodeError(ss);
+        return SECFailure;
     }
     /* build bit vector of peer's supported curve names */
     while (data->len) {
-        PRInt32  curve_name =
-                 ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+        PRInt32 curve_name =
+                ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+        if (curve_name < 0) {
+            return SECFailure; /* fatal alert already sent */
+        }
         if (curve_name > ec_noName && curve_name < ec_pastLastName) {
             peerCurves |= (1U << curve_name);
         }
     }
     /* What curves do we support in common? */
     mutualCurves = ss->ssl3.hs.negotiatedECCurves &= peerCurves;
-    if (!mutualCurves) { /* no mutually supported EC Curves */
-        goto loser;
+    if (!mutualCurves) {
+        /* no mutually supported EC Curves, disable ECC */
+        ssl3_DisableECCSuites(ss, ecSuites);
+        return SECSuccess;
     }
 
     /* if our ECC cert doesn't use one of these supported curves,
      * disable ECC cipher suites that require an ECC cert.
      */
     svrCertCurveName = ssl3_GetSvrCertCurveName(ss);
     if (svrCertCurveName != ec_noName &&
         (mutualCurves & (1U << svrCertCurveName)) != 0) {
         return SECSuccess;
     }
     /* Our EC cert doesn't contain a mutually supported curve.
      * Disable all ECC cipher suites that require an EC cert
      */
     ssl3_DisableECCSuites(ss, ecdh_ecdsa_suites);
     ssl3_DisableECCSuites(ss, ecdhe_ecdsa_suites);
-    return SECFailure;
-
-loser:
-    /* no common curve supported */
-    ssl3_DisableECCSuites(ss, ecSuites);
-    return SECFailure;
+    return SECSuccess;
 }
 
 #endif /* NSS_DISABLE_ECC */
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -1,8 +1,9 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
  * SSL3 Protocol
  *
  * 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/. */
 
 /* TLS extension code moved here from ssl3ecc.c */
@@ -59,20 +60,24 @@ static SECStatus ssl3_ServerHandleNextPr
 static SECStatus ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type,
                                               SECItem *data);
 static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
                                                PRUint32 maxBytes);
 static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append,
                                           PRUint32 maxBytes);
 static PRInt32 ssl3_ServerSendAppProtoXtn(sslSocket *ss, PRBool append,
                                           PRUint32 maxBytes);
-static PRInt32 ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append,
-    PRUint32 maxBytes);
-static SECStatus ssl3_HandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type,
-    SECItem *data);
+static PRInt32 ssl3_ClientSendUseSRTPXtn(sslSocket *ss, PRBool append,
+                                         PRUint32 maxBytes);
+static PRInt32 ssl3_ServerSendUseSRTPXtn(sslSocket *ss, PRBool append,
+                                         PRUint32 maxBytes);
+static SECStatus ssl3_ClientHandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type,
+                                             SECItem *data);
+static SECStatus ssl3_ServerHandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type,
+                                             SECItem *data);
 static PRInt32 ssl3_ServerSendStatusRequestXtn(sslSocket * ss,
     PRBool append, PRUint32 maxBytes);
 static SECStatus ssl3_ServerHandleStatusRequestXtn(sslSocket *ss,
     PRUint16 ex_type, SECItem *data);
 static SECStatus ssl3_ClientHandleStatusRequestXtn(sslSocket *ss,
                                                    PRUint16 ex_type,
                                                    SECItem *data);
 static PRInt32 ssl3_ClientSendStatusRequestXtn(sslSocket * ss, PRBool append,
@@ -242,33 +247,33 @@ static const ssl3HelloExtensionHandler c
 #ifndef NSS_DISABLE_ECC
     { ssl_elliptic_curves_xtn,    &ssl3_HandleSupportedCurvesXtn },
     { ssl_ec_point_formats_xtn,   &ssl3_HandleSupportedPointFormatsXtn },
 #endif
     { ssl_session_ticket_xtn,     &ssl3_ServerHandleSessionTicketXtn },
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { ssl_next_proto_nego_xtn,    &ssl3_ServerHandleNextProtoNegoXtn },
     { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn },
-    { ssl_use_srtp_xtn,           &ssl3_HandleUseSRTPXtn },
+    { ssl_use_srtp_xtn,           &ssl3_ServerHandleUseSRTPXtn },
     { ssl_cert_status_xtn,        &ssl3_ServerHandleStatusRequestXtn },
     { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn },
     { ssl_tls13_draft_version_xtn, &ssl3_ServerHandleDraftVersionXtn },
     { -1, NULL }
 };
 
 /* These two tables are used by the client, to handle server hello
  * extensions. */
 static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
     { ssl_server_name_xtn,        &ssl3_HandleServerNameXtn },
     /* TODO: add a handler for ssl_ec_point_formats_xtn */
     { ssl_session_ticket_xtn,     &ssl3_ClientHandleSessionTicketXtn },
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { ssl_next_proto_nego_xtn,    &ssl3_ClientHandleNextProtoNegoXtn },
     { ssl_app_layer_protocol_xtn, &ssl3_ClientHandleAppProtoXtn },
-    { ssl_use_srtp_xtn,           &ssl3_HandleUseSRTPXtn },
+    { ssl_use_srtp_xtn,           &ssl3_ClientHandleUseSRTPXtn },
     { ssl_cert_status_xtn,        &ssl3_ClientHandleStatusRequestXtn },
     { -1, NULL }
 };
 
 static const ssl3HelloExtensionHandler serverHelloHandlersSSL3[] = {
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { -1, NULL }
 };
@@ -285,17 +290,17 @@ ssl3HelloExtensionSender clientHelloSend
     { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
 #ifndef NSS_DISABLE_ECC
     { ssl_elliptic_curves_xtn,    &ssl3_SendSupportedCurvesXtn },
     { ssl_ec_point_formats_xtn,   &ssl3_SendSupportedPointFormatsXtn },
 #endif
     { ssl_session_ticket_xtn,     &ssl3_SendSessionTicketXtn },
     { ssl_next_proto_nego_xtn,    &ssl3_ClientSendNextProtoNegoXtn },
     { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
-    { ssl_use_srtp_xtn,           &ssl3_SendUseSRTPXtn },
+    { ssl_use_srtp_xtn,           &ssl3_ClientSendUseSRTPXtn },
     { ssl_cert_status_xtn,        &ssl3_ClientSendStatusRequestXtn },
     { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
     { ssl_tls13_draft_version_xtn, &ssl3_ClientSendDraftVersionXtn },
     /* any extra entries will appear as { 0, NULL }    */
 };
 
 static const
 ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
@@ -393,101 +398,100 @@ ssl3_HandleServerNameXtn(sslSocket * ss,
 {
     SECItem *names = NULL;
     PRUint32 listCount = 0, namesPos = 0, i;
     TLSExtensionData *xtnData = &ss->xtnData;
     SECItem  ldata;
     PRInt32  listLenBytes = 0;
 
     if (!ss->sec.isServer) {
-        /* Verify extension_data is empty. */
-        if (data->data || data->len ||
-            !ssl3_ExtensionNegotiated(ss, ssl_server_name_xtn)) {
-            /* malformed or was not initiated by the client.*/
-            return SECFailure;
-        }
-        return SECSuccess;
+        return SECSuccess; /* ignore extension */
     }
 
     /* Server side - consume client data and register server sender. */
     /* do not parse the data if don't have user extension handling function. */
     if (!ss->sniSocketConfig) {
         return SECSuccess;
     }
     /* length of server_name_list */
     listLenBytes = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (listLenBytes == 0 || listLenBytes != data->len) {
+    if (listLenBytes < 0 || listLenBytes != data->len) {
+        (void)ssl3_DecodeError(ss);
         return SECFailure;
     }
+    if (listLenBytes == 0) {
+        return SECSuccess; /* ignore an empty extension */
+    }
     ldata = *data;
     /* Calculate the size of the array.*/
     while (listLenBytes > 0) {
         SECItem litem;
         SECStatus rv;
-        PRInt32  type;
-        /* Name Type (sni_host_name) */
+        PRInt32 type;
+        /* Skip Name Type (sni_host_name); checks are on the second pass */
         type = ssl3_ConsumeHandshakeNumber(ss, 1, &ldata.data, &ldata.len);
-        if (!ldata.len) {
+        if (type < 0) { /* i.e., SECFailure cast to PRint32 */
             return SECFailure;
         }
         rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 2, &ldata.data, &ldata.len);
         if (rv != SECSuccess) {
-            return SECFailure;
+            return rv;
         }
-        /* Adjust total length for cunsumed item, item len and type.*/
+        /* Adjust total length for consumed item, item len and type.*/
         listLenBytes -= litem.len + 3;
         if (listLenBytes > 0 && !ldata.len) {
+            (void)ssl3_DecodeError(ss);
             return SECFailure;
         }
         listCount += 1;
     }
     if (!listCount) {
-        return SECFailure;
+        return SECFailure;  /* nothing we can act on */
     }
     names = PORT_ZNewArray(SECItem, listCount);
     if (!names) {
         return SECFailure;
     }
     for (i = 0;i < listCount;i++) {
         int j;
         PRInt32  type;
         SECStatus rv;
         PRBool nametypePresent = PR_FALSE;
         /* Name Type (sni_host_name) */
         type = ssl3_ConsumeHandshakeNumber(ss, 1, &data->data, &data->len);
         /* Check if we have such type in the list */
         for (j = 0;j < listCount && names[j].data;j++) {
+            /* TODO bug 998524: .type is not assigned a value */
             if (names[j].type == type) {
                 nametypePresent = PR_TRUE;
                 break;
             }
         }
         /* HostName (length and value) */
         rv = ssl3_ConsumeHandshakeVariable(ss, &names[namesPos], 2,
                                            &data->data, &data->len);
         if (rv != SECSuccess) {
-            goto loser;
+            PORT_Assert(0);
+            PORT_Free(names);
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            return rv;
         }
         if (nametypePresent == PR_FALSE) {
             namesPos += 1;
         }
     }
     /* Free old and set the new data. */
     if (xtnData->sniNameArr) {
         PORT_Free(ss->xtnData.sniNameArr);
     }
     xtnData->sniNameArr = names;
     xtnData->sniNameArrSize = namesPos;
     xtnData->negotiated[xtnData->numNegotiated++] = ssl_server_name_xtn;
 
     return SECSuccess;
-
-loser:
-    PORT_Free(names);
-    return SECFailure;
 }
 
 /* Called by both clients and servers.
  * Clients sends a filled in session ticket if one is available, and otherwise
  * sends an empty ticket.  Servers always send empty tickets.
  */
 PRInt32
 ssl3_SendSessionTicketXtn(
@@ -598,109 +602,114 @@ ssl3_ValidateNextProtoNego(const unsigne
     unsigned int offset = 0;
 
     while (offset < length) {
         unsigned int newOffset = offset + 1 + (unsigned int) data[offset];
         /* Reject embedded nulls to protect against buggy applications that
          * store protocol identifiers in null-terminated strings.
          */
         if (newOffset > length || data[offset] == 0) {
-            PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
             return SECFailure;
         }
         offset = newOffset;
     }
 
-    if (offset > length) {
-        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
-        return SECFailure;
-    }
-
     return SECSuccess;
 }
 
 /* protocol selection handler for ALPN (server side) and NPN (client side) */
 static SECStatus
 ssl3_SelectAppProtocol(sslSocket *ss, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
     unsigned char resultBuffer[255];
     SECItem result = { siBuffer, resultBuffer, 0 };
 
     rv = ssl3_ValidateNextProtoNego(data->data, data->len);
-    if (rv != SECSuccess)
+    if (rv != SECSuccess) {
+        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+        (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
         return rv;
+    }
 
     PORT_Assert(ss->nextProtoCallback);
     rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len,
-                               result.data, &result.len, sizeof resultBuffer);
-    if (rv != SECSuccess)
-        return rv;
-    /* If the callback wrote more than allowed to |result| it has corrupted our
-     * stack. */
-    if (result.len > sizeof resultBuffer) {
-        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+                               result.data, &result.len, sizeof(resultBuffer));
+    if (rv != SECSuccess) {
+        /* Expect callback to call PORT_SetError() */
+        (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
         return SECFailure;
     }
 
+    /* If the callback wrote more than allowed to |result| it has corrupted our
+     * stack. */
+    if (result.len > sizeof(resultBuffer)) {
+        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+        /* TODO: crash */
+        return SECFailure;
+    }
+
+    SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+
     if (ex_type == ssl_app_layer_protocol_xtn &&
         ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NEGOTIATED) {
-        /* The callback might say OK, but then it's picked a default.
-         * That's OK for NPN, but not ALPN. */
-        SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+        /* The callback might say OK, but then it picks a default value - one
+         * that was not listed.  That's OK for NPN, but not ALPN. */
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL);
         (void)SSL3_SendAlert(ss, alert_fatal, no_application_protocol);
         return SECFailure;
     }
 
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
-
-    SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
     return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result);
 }
 
 /* handle an incoming ALPN extension at the server */
 static SECStatus
 ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
 {
     int count;
     SECStatus rv;
 
     /* We expressly don't want to allow ALPN on renegotiation,
      * despite it being permitted by the spec. */
     if (ss->firstHsDone || data->len == 0) {
         /* Clients MUST send a non-empty ALPN extension. */
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+        (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
         return SECFailure;
     }
 
-    /* unlike NPN, ALPN has extra redundant length information so that
-     * the extension is the same in both ClientHello and ServerHello */
+    /* Unlike NPN, ALPN has extra redundant length information so that
+     * the extension is the same in both ClientHello and ServerHello. */
     count = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (count < 0) {
-        return SECFailure; /* fatal alert was sent */
-    }
     if (count != data->len) {
-        return ssl3_DecodeError(ss);
+        (void)ssl3_DecodeError(ss);
+        return SECFailure;
     }
 
     if (!ss->nextProtoCallback) {
         /* we're not configured for it */
         return SECSuccess;
     }
 
     rv = ssl3_SelectAppProtocol(ss, ex_type, data);
     if (rv != SECSuccess) {
       return rv;
     }
 
     /* prepare to send back a response, if we negotiated */
     if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) {
-        return ssl3_RegisterServerHelloExtensionSender(
+        rv = ssl3_RegisterServerHelloExtensionSender(
             ss, ex_type, ssl3_ServerSendAppProtoXtn);
+        if (rv != SECSuccess) {
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
+            return rv;
+        }
     }
     return SECSuccess;
 }
 
 static SECStatus
 ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
                                   SECItem *data)
 {
@@ -708,62 +717,73 @@ ssl3_ClientHandleNextProtoNegoXtn(sslSoc
 
     if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) {
         /* If the server negotiated ALPN then it has already told us what
          * protocol to use, so it doesn't make sense for us to try to negotiate
          * a different one by sending the NPN handshake message. However, if
          * we've negotiated NPN then we're required to send the NPN handshake
          * message. Thus, these two extensions cannot both be negotiated on the
          * same connection. */
-        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        PORT_SetError(SSL_ERROR_BAD_SERVER);
+        (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
         return SECFailure;
     }
 
     /* We should only get this call if we sent the extension, so
      * ss->nextProtoCallback needs to be non-NULL.  However, it is possible
      * that an application erroneously cleared the callback between the time
      * we sent the ClientHello and now. */
     if (!ss->nextProtoCallback) {
+        PORT_Assert(0);
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK);
+        (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
         return SECFailure;
     }
 
     return ssl3_SelectAppProtocol(ss, ex_type, data);
 }
 
 static SECStatus
 ssl3_ClientHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
 {
-    const unsigned char* d = data->data;
-    PRUint16 name_list_len;
+    SECStatus rv;
+    PRInt32 list_len;
     SECItem protocol_name;
 
     if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     /* The extension data from the server has the following format:
      *   uint16 name_list_len;
-     *   uint8 len;
+     *   uint8 len;  // where len >= 1
      *   uint8 protocol_name[len]; */
     if (data->len < 4 || data->len > 2 + 1 + 255) {
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+        (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
         return SECFailure;
     }
 
-    name_list_len = ((PRUint16) d[0]) << 8 |
-                    ((PRUint16) d[1]);
-    if (name_list_len != data->len - 2 || d[2] != data->len - 3) {
+    list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+    /* The list has to be the entire extension. */
+    if (list_len != data->len) {
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+        (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
         return SECFailure;
     }
 
-    protocol_name.data = data->data + 3;
-    protocol_name.len = data->len - 3;
+    rv = ssl3_ConsumeHandshakeVariable(ss, &protocol_name, 1,
+                                       &data->data, &data->len);
+    /* The list must have exactly one value. */
+    if (rv != SECSuccess || data->len != 0) {
+        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+        (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
+        return SECFailure;
+    }
 
     SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
     ss->ssl3.nextProtoState = SSL_NEXT_PROTO_SELECTED;
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
     return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &protocol_name);
 }
 
 static PRInt32
@@ -1381,18 +1401,19 @@ ssl3_ServerHandleSessionTicketXtn(sslSoc
 {
     SECStatus rv;
     SECItem *decrypted_state = NULL;
     SessionTicket *parsed_session_ticket = NULL;
     sslSessionID *sid = NULL;
     SSL3Statistics *ssl3stats;
 
     /* Ignore the SessionTicket extension if processing is disabled. */
-    if (!ss->opt.enableSessionTickets)
+    if (!ss->opt.enableSessionTickets) {
         return SECSuccess;
+    }
 
     /* Keep track of negotiated extensions. */
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
 
     /* Parse the received ticket sent in by the client.  We are
      * lenient about some parse errors, falling back to a fullshake
      * instead of terminating the current connection.
      */
@@ -1440,18 +1461,19 @@ ssl3_ServerHandleSessionTicketXtn(sslSoc
             ssl_FreeSID(ss->sec.ci.sid);
             ss->sec.ci.sid = NULL;
         }
 
         extension_data.data = data->data; /* Keep a copy for future use. */
         extension_data.len = data->len;
 
         if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket)
-            != SECSuccess)
-            return SECFailure;
+            != SECSuccess) {
+            return SECSuccess; /* Pretend it isn't there */
+        }
 
         /* Get session ticket keys. */
 #ifndef NO_PKCS11_BYPASS
         if (ss->opt.bypassPKCS11) {
             rv = ssl3_GetSessionTicketKeys(&aes_key, &aes_key_length,
                 &mac_key, &mac_key_length);
         } else
 #endif
@@ -1869,38 +1891,46 @@ ssl3_HandleHelloExtensions(sslSocket *ss
         /* Get the extension's type field */
         extension_type = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
         if (extension_type < 0)  /* failure to decode extension_type */
             return SECFailure;   /* alert already sent */
 
         /* get the data for this extension, so we can pass it or skip it. */
         rv = ssl3_ConsumeHandshakeVariable(ss, &extension_data, 2, b, length);
         if (rv != SECSuccess)
-            return rv;
+            return rv; /* alert already sent */
 
         /* Check whether the server sent an extension which was not advertised
          * in the ClientHello.
          */
         if (!ss->sec.isServer &&
-            !ssl3_ClientExtensionAdvertised(ss, extension_type))
-            return SECFailure;  /* TODO: send unsupported_extension alert */
+            !ssl3_ClientExtensionAdvertised(ss, extension_type)) {
+            (void)SSL3_SendAlert(ss, alert_fatal, unsupported_extension);
+            return SECFailure;
+        }
 
         /* Check whether an extension has been sent multiple times. */
-        if (ssl3_ExtensionNegotiated(ss, extension_type))
+        if (ssl3_ExtensionNegotiated(ss, extension_type)) {
+            (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
             return SECFailure;
+        }
 
         /* find extension_type in table of Hello Extension Handlers */
         for (handler = handlers; handler->ex_type >= 0; handler++) {
             /* if found, call this handler */
             if (handler->ex_type == extension_type) {
                 rv = (*handler->ex_handler)(ss, (PRUint16)extension_type,
                                                         &extension_data);
-                /* Ignore this result */
-                /* Treat all bad extensions as unrecognized types. */
-                break;
+                if (rv != SECSuccess) {
+                    if (!ss->ssl3.fatalAlertSent) {
+                        /* send a generic alert if the handler didn't already */
+                        (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
+                    }
+                    return SECFailure;
+                }
             }
         }
     }
     return SECSuccess;
 }
 
 /* Add a callback function to the table of senders of server hello extensions.
  */
@@ -2022,201 +2052,209 @@ ssl3_HandleRenegotiationInfoXtn(sslSocke
 {
     SECStatus rv = SECSuccess;
     PRUint32 len = 0;
 
     if (ss->firstHsDone) {
         len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes
                                : ss->ssl3.hs.finishedBytes * 2;
     }
-    if (data->len != 1 + len  ||
-        data->data[0] != len  || (len &&
-        NSS_SecureMemcmp(ss->ssl3.hs.finishedMsgs.data,
-                         data->data + 1, len))) {
-        /* Can we do this here? Or, must we arrange for the caller to do it? */
+    if (data->len != 1 + len || data->data[0] != len ) {
+        (void)ssl3_DecodeError(ss);
+        return SECFailure;
+    }
+    if (len && NSS_SecureMemcmp(ss->ssl3.hs.finishedMsgs.data,
+                                data->data + 1, len)) {
+        PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
         (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
-        PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
         return SECFailure;
     }
     /* remember that we got this extension and it was correct. */
     ss->peerRequestedProtection = 1;
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
     if (ss->sec.isServer) {
         /* prepare to send back the appropriate response */
         rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type,
-                                             ssl3_SendRenegotiationInfoXtn);
+                                                     ssl3_SendRenegotiationInfoXtn);
     }
     return rv;
 }
 
 static PRInt32
-ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes)
+ssl3_ClientSendUseSRTPXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes)
 {
     PRUint32 ext_data_len;
     PRInt16 i;
     SECStatus rv;
 
     if (!ss)
         return 0;
 
-    if (!ss->sec.isServer) {
-        /* Client side */
-
-        if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount)
-            return 0;  /* Not relevant */
-
-        ext_data_len = 2 + 2 * ss->ssl3.dtlsSRTPCipherCount + 1;
+    if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount)
+        return 0;  /* Not relevant */
 
-        if (append && maxBytes >= 4 + ext_data_len) {
-            /* Extension type */
-            rv = ssl3_AppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2);
-            if (rv != SECSuccess) return -1;
-            /* Length of extension data */
-            rv = ssl3_AppendHandshakeNumber(ss, ext_data_len, 2);
-            if (rv != SECSuccess) return -1;
-            /* Length of the SRTP cipher list */
-            rv = ssl3_AppendHandshakeNumber(ss,
-                                            2 * ss->ssl3.dtlsSRTPCipherCount,
-                                            2);
-            if (rv != SECSuccess) return -1;
-            /* The SRTP ciphers */
-            for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) {
-                rv = ssl3_AppendHandshakeNumber(ss,
-                                                ss->ssl3.dtlsSRTPCiphers[i],
-                                                2);
-            }
-            /* Empty MKI value */
-            ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
+    ext_data_len = 2 + 2 * ss->ssl3.dtlsSRTPCipherCount + 1;
 
-            ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
-                ssl_use_srtp_xtn;
-        }
-
-        return 4 + ext_data_len;
-    }
-
-    /* Server side */
-    if (append && maxBytes >= 9) {
+    if (append && maxBytes >= 4 + ext_data_len) {
         /* Extension type */
         rv = ssl3_AppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2);
         if (rv != SECSuccess) return -1;
         /* Length of extension data */
-        rv = ssl3_AppendHandshakeNumber(ss, 5, 2);
+        rv = ssl3_AppendHandshakeNumber(ss, ext_data_len, 2);
         if (rv != SECSuccess) return -1;
         /* Length of the SRTP cipher list */
-        rv = ssl3_AppendHandshakeNumber(ss, 2, 2);
+        rv = ssl3_AppendHandshakeNumber(ss,
+                                        2 * ss->ssl3.dtlsSRTPCipherCount,
+                                        2);
         if (rv != SECSuccess) return -1;
-        /* The selected cipher */
-        rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.dtlsSRTPCipherSuite, 2);
-        if (rv != SECSuccess) return -1;
+        /* The SRTP ciphers */
+        for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) {
+            rv = ssl3_AppendHandshakeNumber(ss,
+                                            ss->ssl3.dtlsSRTPCiphers[i],
+                                            2);
+        }
         /* Empty MKI value */
         ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
+
+        ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
+                ssl_use_srtp_xtn;
     }
 
+    return 4 + ext_data_len;
+}
+
+static PRInt32
+ssl3_ServerSendUseSRTPXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes)
+{
+    SECStatus rv;
+
+    /* Server side */
+    if (!append || maxBytes < 9) {
+        return 9;
+    }
+
+    /* Extension type */
+    rv = ssl3_AppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2);
+    if (rv != SECSuccess) return -1;
+    /* Length of extension data */
+    rv = ssl3_AppendHandshakeNumber(ss, 5, 2);
+    if (rv != SECSuccess) return -1;
+    /* Length of the SRTP cipher list */
+    rv = ssl3_AppendHandshakeNumber(ss, 2, 2);
+    if (rv != SECSuccess) return -1;
+    /* The selected cipher */
+    rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.dtlsSRTPCipherSuite, 2);
+    if (rv != SECSuccess) return -1;
+    /* Empty MKI value */
+    ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
+
     return 9;
 }
 
 static SECStatus
-ssl3_HandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
+ssl3_ClientHandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
+{
+    SECStatus rv;
+    SECItem ciphers = {siBuffer, NULL, 0};
+    PRUint16 i;
+    PRUint16 cipher = 0;
+    PRBool found = PR_FALSE;
+    SECItem litem;
+
+    if (!data->data || !data->len) {
+        (void)ssl3_DecodeError(ss);
+        return SECFailure;
+    }
+
+    /* Get the cipher list */
+    rv = ssl3_ConsumeHandshakeVariable(ss, &ciphers, 2,
+                                       &data->data, &data->len);
+    if (rv != SECSuccess) {
+        return SECFailure;  /* fatal alert already sent */
+    }
+    /* Now check that the server has picked just 1 (i.e., len = 2) */
+    if (ciphers.len != 2) {
+        (void)ssl3_DecodeError(ss);
+        return SECFailure;
+    }
+
+    /* Get the selected cipher */
+    cipher = (ciphers.data[0] << 8) | ciphers.data[1];
+
+    /* Now check that this is one of the ciphers we offered */
+    for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) {
+        if (cipher == ss->ssl3.dtlsSRTPCiphers[i]) {
+            found = PR_TRUE;
+            break;
+        }
+    }
+
+    if (!found) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+        (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
+        return SECFailure;
+    }
+
+    /* Get the srtp_mki value */
+    rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 1,
+                                       &data->data, &data->len);
+    if (rv != SECSuccess) {
+        return SECFailure; /* alert already sent */
+    }
+
+    /* We didn't offer an MKI, so this must be 0 length */
+    if (litem.len != 0) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+        (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
+        return SECFailure;
+    }
+
+    /* extra trailing bytes */
+    if (data->len != 0) {
+        (void)ssl3_DecodeError(ss);
+        return SECFailure;
+    }
+
+    /* OK, this looks fine. */
+    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_use_srtp_xtn;
+    ss->ssl3.dtlsSRTPCipherSuite = cipher;
+    return SECSuccess;
+}
+
+static SECStatus
+ssl3_ServerHandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
     SECItem ciphers = {siBuffer, NULL, 0};
     PRUint16 i;
     unsigned int j;
     PRUint16 cipher = 0;
     PRBool found = PR_FALSE;
     SECItem litem;
 
-    if (!ss->sec.isServer) {
-        /* Client side */
-        if (!data->data || !data->len) {
-            /* malformed */
-            return SECFailure;
-        }
-
-        /* Get the cipher list */
-        rv = ssl3_ConsumeHandshakeVariable(ss, &ciphers, 2,
-                                           &data->data, &data->len);
-        if (rv != SECSuccess) {
-            return SECFailure;
-        }
-        /* Now check that the number of ciphers listed is 1 (len = 2) */
-        if (ciphers.len != 2) {
-            return SECFailure;
-        }
-
-        /* Get the selected cipher */
-        cipher = (ciphers.data[0] << 8) | ciphers.data[1];
-
-        /* Now check that this is one of the ciphers we offered */
-        for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) {
-            if (cipher == ss->ssl3.dtlsSRTPCiphers[i]) {
-                found = PR_TRUE;
-                break;
-            }
-        }
-
-        if (!found) {
-            return SECFailure;
-        }
-
-        /* Get the srtp_mki value */
-        rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 1,
-                                           &data->data, &data->len);
-        if (rv != SECSuccess) {
-            return SECFailure;
-        }
-
-        /* We didn't offer an MKI, so this must be 0 length */
-        /* XXX RFC 5764 Section 4.1.3 says:
-         *   If the client detects a nonzero-length MKI in the server's
-         *   response that is different than the one the client offered,
-         *   then the client MUST abort the handshake and SHOULD send an
-         *   invalid_parameter alert.
-         *
-         * Due to a limitation of the ssl3_HandleHelloExtensions function,
-         * returning SECFailure here won't abort the handshake.  It will
-         * merely cause the use_srtp extension to be not negotiated.  We
-         * should fix this.  See NSS bug 753136.
-         */
-        if (litem.len != 0) {
-            return SECFailure;
-        }
-
-        if (data->len != 0) {
-            /* malformed */
-            return SECFailure;
-        }
-
-        /* OK, this looks fine. */
-        ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_use_srtp_xtn;
-        ss->ssl3.dtlsSRTPCipherSuite = cipher;
-        return SECSuccess;
-    }
-
-    /* Server side */
     if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount) {
         /* Ignore the extension if we aren't doing DTLS or no DTLS-SRTP
          * preferences have been set. */
         return SECSuccess;
     }
 
     if (!data->data || data->len < 5) {
-        /* malformed */
+        (void)ssl3_DecodeError(ss);
         return SECFailure;
     }
 
     /* Get the cipher list */
     rv = ssl3_ConsumeHandshakeVariable(ss, &ciphers, 2,
                                        &data->data, &data->len);
     if (rv != SECSuccess) {
-        return SECFailure;
+        return SECFailure; /* alert already sent */
     }
     /* Check that the list is even length */
     if (ciphers.len % 2) {
+        (void)ssl3_DecodeError(ss);
         return SECFailure;
     }
 
     /* Walk through the offered list and pick the most preferred of our
      * ciphers, if any */
     for (i = 0; !found && i < ss->ssl3.dtlsSRTPCipherCount; i++) {
         for (j = 0; j + 1 < ciphers.len; j += 2) {
             cipher = (ciphers.data[j] << 8) | ciphers.data[j + 1];
@@ -2229,31 +2267,32 @@ ssl3_HandleUseSRTPXtn(sslSocket * ss, PR
 
     /* Get the srtp_mki value */
     rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 1, &data->data, &data->len);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     if (data->len != 0) {
-        return SECFailure; /* Malformed */
+        (void)ssl3_DecodeError(ss); /* trailing bytes */
+        return SECFailure;
     }
 
     /* Now figure out what to do */
     if (!found) {
-        /* No matching ciphers */
+        /* No matching ciphers, pretend we don't support use_srtp */
         return SECSuccess;
     }
 
     /* OK, we have a valid cipher and we've selected it */
     ss->ssl3.dtlsSRTPCipherSuite = cipher;
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_use_srtp_xtn;
 
     return ssl3_RegisterServerHelloExtensionSender(ss, ssl_use_srtp_xtn,
-                                                   ssl3_SendUseSRTPXtn);
+                                                   ssl3_ServerSendUseSRTPXtn);
 }
 
 /* ssl3_ServerHandleSigAlgsXtn handles the signature_algorithms extension
  * from a client.
  * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 static SECStatus
 ssl3_ServerHandleSigAlgsXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
 {
@@ -2262,40 +2301,40 @@ ssl3_ServerHandleSigAlgsXtn(sslSocket * 
     const unsigned char *b;
     unsigned int numAlgorithms, i, j;
 
     /* Ignore this extension if we aren't doing TLS 1.2 or greater. */
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_2) {
         return SECSuccess;
     }
 
-    /* Keep track of negotiated extensions. */
-    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
-
     rv = ssl3_ConsumeHandshakeVariable(ss, &algorithms, 2, &data->data,
                                        &data->len);
     if (rv != SECSuccess) {
         return SECFailure;
     }
     /* Trailing data, empty value, or odd-length value is invalid. */
     if (data->len != 0 || algorithms.len == 0 || (algorithms.len & 1) != 0) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+        (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
         return SECFailure;
     }
 
     numAlgorithms = algorithms.len/2;
 
     /* We don't care to process excessive numbers of algorithms. */
     if (numAlgorithms > 512) {
         numAlgorithms = 512;
     }
 
     ss->ssl3.hs.clientSigAndHash =
             PORT_NewArray(SSL3SignatureAndHashAlgorithm, numAlgorithms);
     if (!ss->ssl3.hs.clientSigAndHash) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+        (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
         return SECFailure;
     }
     ss->ssl3.hs.numClientSigAndHash = 0;
 
     b = algorithms.data;
     for (i = j = 0; i < numAlgorithms; i++) {
         unsigned char tls_hash = *(b++);
         unsigned char tls_sig = *(b++);
@@ -2315,16 +2354,18 @@ ssl3_ServerHandleSigAlgsXtn(sslSocket * 
 
     if (!ss->ssl3.hs.numClientSigAndHash) {
         /* We didn't understand any of the client's requested signature
          * formats. We'll use the defaults. */
         PORT_Free(ss->ssl3.hs.clientSigAndHash);
         ss->ssl3.hs.clientSigAndHash = NULL;
     }
 
+    /* Keep track of negotiated extensions. */
+    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
     return SECSuccess;
 }
 
 /* ssl3_ClientSendSigAlgsXtn sends the signature_algorithm extension for TLS
  * 1.2 ClientHellos. */
 static PRInt32
 ssl3_ClientSendSigAlgsXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
 {
@@ -2478,46 +2519,37 @@ ssl3_ServerHandleDraftVersionXtn(sslSock
 {
     PRInt32 draft_version;
 
     /* Ignore this extension if we aren't doing TLS 1.3 */
     if (ss->version != SSL_LIBRARY_VERSION_TLS_1_3) {
         return SECSuccess;
     }
 
-    if (data->len != 2)
-        goto loser;
+    if (data->len != 2) {
+        (void)ssl3_DecodeError(ss);
+        return SECFailure;
+    }
 
     /* Get the draft version out of the handshake */
     draft_version = ssl3_ConsumeHandshakeNumber(ss, 2,
                                                 &data->data, &data->len);
     if (draft_version < 0) {
-        goto loser;
+        return SECFailure;
     }
 
     /*  Keep track of negotiated extensions. */
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
 
-    /* Compare the version */
     if (draft_version != TLS_1_3_DRAFT_VERSION) {
+        /*
+         * Incompatible/broken TLS 1.3 implementation. Fall back to TLS 1.2.
+         * TODO(ekr@rtfm.com): It's not entirely clear it's safe to roll back
+         * here. Need to double-check.
+         */
         SSL_TRC(30, ("%d: SSL3[%d]: Incompatible version of TLS 1.3 (%d), "
                      "expected %d",
                      SSL_GETPID(), ss->fd, draft_version, TLS_1_3_DRAFT_VERSION));
-        goto loser;
+        ss->version = SSL_LIBRARY_VERSION_TLS_1_2;
     }
 
     return SECSuccess;
-
-loser:
-    /*
-     * Incompatible/broken TLS 1.3 implementation. Fall back to TLS 1.2.
-     * TODO(ekr@rtfm.com): It's not entirely clear it's safe to roll back
-     * here. Need to double-check.
-     * TODO(ekr@rtfm.com): Currently we fall back even on broken extensions.
-     * because SECFailure does not cause handshake failures. See bug
-     * 753136.
-     */
-    SSL_TRC(30, ("%d: SSL3[%d]: Rolling back to TLS 1.2", SSL_GETPID(), ss->fd));
-    ss->version = SSL_LIBRARY_VERSION_TLS_1_2;
-
-    return SECSuccess;
 }
-
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -976,16 +976,17 @@ struct ssl3StateStr {
     SSLNextProtoState    nextProtoState;
 
     PRUint16             mtu;   /* Our estimate of the MTU */
 
     /* DTLS-SRTP cipher suite preferences (if any) */
     PRUint16             dtlsSRTPCiphers[MAX_DTLS_SRTP_CIPHER_SUITES];
     PRUint16             dtlsSRTPCipherCount;
     PRUint16             dtlsSRTPCipherSuite;	/* 0 if not selected */
+    PRBool               fatalAlertSent;
 };
 
 #define DTLS_MAX_MTU  1500      /* Ethernet MTU but without subtracting the
 				 * headers, so slightly larger than expected */
 #define IS_DTLS(ss) (ss->protocolVariant == ssl_variant_datagram)
 
 typedef struct {
     SSL3ContentType      type;
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION  "3.18"
+#define NSSUTIL_VERSION  "3.18.1 Beta"
 #define NSSUTIL_VMAJOR   3
 #define NSSUTIL_VMINOR   18
-#define NSSUTIL_VPATCH   0
-#define NSSUTIL_VBUILD   2
-#define NSSUTIL_BETA     PR_FALSE
+#define NSSUTIL_VPATCH   1
+#define NSSUTIL_VBUILD   0
+#define NSSUTIL_BETA     PR_TRUE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);