Index: external_tests/ssl_gtest/ssl_internal_unittest.cc |
diff --git a/external_tests/ssl_gtest/ssl_internal_unittest.cc b/external_tests/ssl_gtest/ssl_internal_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7e2a32be7cf5f8c33378eaa8e489eee45a89b5ff |
--- /dev/null |
+++ b/external_tests/ssl_gtest/ssl_internal_unittest.cc |
@@ -0,0 +1,233 @@ |
+// -*- 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 <string.h> |
+#ifdef XP_UNIX |
+#include <sys/resource.h> |
+#endif |
+ |
+#include <memory> // for unique_ptr |
+ |
+#include "prthread.h" |
+ |
+#include "gtest_utils.h" |
+#include "keyhi.h" |
+#include "scoped_ptrs.h" |
+#include "ssl.h" |
+#include "sslerr.h" |
+#include "sslimpl.h" |
+#include "test_io.h" |
+ |
+namespace nss_test { |
+ |
+// This can't be defined in scoped_ptrs.h because that's shared with |
+// other tests that aren't set up to include sslimpl.h to get the |
+// ssl3* internal declarations. |
+struct ScopedMaybeDeleteKeyPair { |
+ void operator()(ssl3KeyPair* ptr) { |
+ if (ptr) { |
+ ssl3_FreeKeyPair(ptr); |
+ } |
+ } |
+}; |
+typedef std::unique_ptr<ssl3KeyPair, ScopedMaybeDeleteKeyPair> ScopedKeyPair; |
+ |
+// DEBUG_ASSERT_DEATH, for operations that should crash on debug |
+// builds and do something expected on non-debug builds. |
+// TODO(jld@mozilla.com), bug 1243238: move this into a common location. |
+#ifdef DEBUG |
+#ifdef XP_UNIX |
+// Unix + debug: check that assertion crashes as expected; suppress |
+// core dumps, which the test harness would treat as a failure. |
+class SuppressCoreDump { |
+public: |
+ SuppressCoreDump() { |
+ if (getrlimit(RLIMIT_CORE, &saved_limit_) != 0) { |
+ saved_limit_.rlim_cur = saved_limit_.rlim_max = RLIM_INFINITY; |
+ } |
+ struct ::rlimit new_limit = saved_limit_; |
+ new_limit.rlim_cur = 0; |
+ setrlimit(RLIMIT_CORE, &new_limit); |
+ } |
+ |
+ ~SuppressCoreDump() { |
+ setrlimit(RLIMIT_CORE, &saved_limit_); |
+ } |
+ |
+private: |
+ struct ::rlimit saved_limit_; |
+}; |
+ |
+#define DEBUG_ASSERT_DEATH(stmt, regex) ASSERT_DEATH_IF_SUPPORTED({ \ |
+ SuppressCoreDump _coreDumpGuard; \ |
+ stmt; \ |
+ }, regex) |
+#else |
+// Not-Unix + debug: check that assertion crashes; core dumps not an issue. |
+#define DEBUG_ASSERT_DEATH(stmt, regex) ASSERT_DEATH_IF_SUPPORTED(stmt, regex) |
+#endif |
+#else |
+// Non-debug: check that the bad thing *doesn't* crash by doing it. |
+#define DEBUG_ASSERT_DEATH(stmt, regex) stmt |
+#endif |
+ |
+class InternalSocketTest : public ::testing::Test { |
+public: |
+ InternalSocketTest() : fd_(nullptr), ss_(nullptr) { } |
+ ~InternalSocketTest() { } |
+ |
+ void SetUp() { |
+ fd_.reset(DummyPrSocket::CreateFD("fake", STREAM)); |
+ ASSERT_NE(nullptr, fd_); |
+ ASSERT_EQ(fd_.get(), SSL_ImportFD(nullptr, fd_.get())); |
+ ss_ = ssl_FindSocket(fd_.get()); |
+ ASSERT_NE(nullptr, ss_); |
+ } |
+ |
+protected: |
+ ScopedPRFileDesc fd_; |
+ sslSocket *ss_; // The sslSocket is owned by the PRFileDesc. |
+}; |
+ |
+class InternalKeyPairTest : public ::testing::Test { |
+public: |
+ InternalKeyPairTest() : keys_(nullptr) { } |
+ ~InternalKeyPairTest() { } |
+ |
+ void SetUp() { |
+ static const ECName curve = ec_secp256r1; |
+ ScopedSECItem ecParams(SECITEM_AllocItem(nullptr, // no arena |
+ nullptr, // not reallocating |
+ 0)); // length |
+ ASSERT_TRUE(ecParams); |
+ ASSERT_EQ(SECSuccess, |
+ ssl3_ECName2Params(nullptr, // no arena |
+ curve, ecParams.get())); |
+ EXPECT_NE(nullptr, ecParams->data); |
+ EXPECT_NE(0U, ecParams->len); |
+ |
+ |
+ SECKEYPublicKey *tmpPubKey; |
+ ScopedSECKEYPrivateKey |
+ privKey(SECKEY_CreateECPrivateKey(ecParams.get(), |
+ &tmpPubKey, |
+ nullptr)); // no UI context |
+ ScopedSECKEYPublicKey pubKey(tmpPubKey); |
+ |
+ ASSERT_TRUE(privKey); |
+ ASSERT_TRUE(pubKey); |
+ |
+ keys_.reset(ssl3_NewKeyPair(privKey.release(), pubKey.release())); |
+ ASSERT_TRUE(keys_); |
+ } |
+ |
+protected: |
+ ScopedKeyPair keys_; |
+}; |
+ |
+template<class F> |
+static void |
+ThreadFunctionalMain(void *vp) { |
+ (*static_cast<const F*>(vp))(); |
+} |
+ |
+template<class F> |
+static void |
+RunOnThreads(size_t numThreads, const F& func) |
+{ |
+ void* vp = const_cast<void*>(static_cast<const void*>(&func)); |
+ std::unique_ptr<PRThread*[]> threads(new PRThread*[numThreads]); |
+ |
+ for (size_t i = 0; i < numThreads; ++i) { |
+ threads[i] = PR_CreateThread(PR_SYSTEM_THREAD, |
+ ThreadFunctionalMain<F>, |
+ vp, |
+ PR_PRIORITY_NORMAL, |
+ PR_GLOBAL_THREAD, |
+ PR_JOINABLE_THREAD, |
+ 0); // use default stack size |
+ ASSERT_NE(nullptr, threads[i]); |
+ } |
+ for (size_t i = 0; i < numThreads; ++i) { |
+ EXPECT_EQ(PR_SUCCESS, PR_JoinThread(threads[i])); |
+ } |
+} |
+ |
+TEST(SSL3Random, SmokeTest) { |
+ // Check that two successive random numbers aren't equal. This is |
+ // wrong with probability 2**-256 per test run, which is negligible. |
+ SSL3Random r0; |
+ ASSERT_EQ(SECSuccess, ssl3_GetNewRandom(&r0)); |
+ SSL3Random r1; |
+ ASSERT_EQ(SECSuccess, ssl3_GetNewRandom(&r1)); |
+ EXPECT_NE(0, memcmp(&r0, &r1, SSL3_RANDOM_LENGTH)); |
+ |
+ // ssl3_GetNewRandom uses the "rand" field, but other code memcpy()s |
+ // the first SSL3_RANDOM_LENGTH bytes, so make sure that does what's |
+ // expected: |
+ ASSERT_LE(static_cast<size_t>(SSL3_RANDOM_LENGTH), sizeof(SSL3Random)); |
+ ASSERT_EQ(static_cast<void*>(&r0), static_cast<void*>(r0.rand)); |
+} |
+ |
+typedef InternalSocketTest InternalSocketDeathTest; |
+ |
+TEST_F(InternalSocketDeathTest, DoubleUnlockReader) { |
+ // On non-debug builds, an excess unlock is ignored. There isn't a |
+ // clean way to verify that directly, but the second loop iteration |
+ // will at least show that further lock operations don't crash. |
+ for (int i = 0; i < 2; ++i) { |
+ SSL_LOCK_READER(ss_); |
+ SSL_UNLOCK_READER(ss_); |
+ DEBUG_ASSERT_DEATH(SSL_UNLOCK_READER(ss_), "Assertion failure:"); |
+ } |
+} |
+ |
+TEST_F(InternalSocketDeathTest, DoubleUnlock1stHandshake) { |
+ // Similarly to the above test, except that this lock supports an |
+ // "is this held" operation, so on non-debug builds the second loop |
+ // iteration will better verify that the extra unlock is ignored as |
+ // expected. |
+ for (int i = 0; i < 2; ++i) { |
+ EXPECT_FALSE(ssl_Have1stHandshakeLock(ss_)); |
+ ssl_Get1stHandshakeLock(ss_); |
+ EXPECT_TRUE(ssl_Have1stHandshakeLock(ss_)); |
+ ssl_Release1stHandshakeLock(ss_); |
+ EXPECT_FALSE(ssl_Have1stHandshakeLock(ss_)); |
+ DEBUG_ASSERT_DEATH(ssl_Release1stHandshakeLock(ss_), "Assertion failure:"); |
+ } |
+} |
+ |
+TEST_F(InternalKeyPairTest, RefCountSimple) { |
+ EXPECT_EQ(1, keys_->refCount); |
+ EXPECT_EQ(keys_.get(), ssl3_GetKeyPairRef(keys_.get())); |
+ EXPECT_EQ(2, keys_->refCount); |
+ ssl3_FreeKeyPair(keys_.get()); |
+ EXPECT_EQ(1, keys_->refCount); |
+} |
+ |
+TEST_F(InternalKeyPairTest, RefCountThreaded) { |
+ static const size_t numThreads = 5; |
+ static const size_t iterations = 1000000; |
+ ssl3KeyPair *const keys = keys_.get(); |
+ |
+ RunOnThreads(numThreads, [=]{ |
+ for (size_t i = 0; i < iterations; ++i) { |
+ ssl3_GetKeyPairRef(keys); |
+ } |
+ }); |
+ |
+ ASSERT_EQ(1 + numThreads * iterations, static_cast<size_t>(keys->refCount)); |
+ |
+ RunOnThreads(numThreads, [=]{ |
+ for (size_t i = 0; i < iterations; ++i) { |
+ ssl3_FreeKeyPair(keys); |
+ } |
+ }); |
+ |
+ EXPECT_EQ(1, keys->refCount); |
+} |
+ |
+} // namespace nss_test |