bug 1424085 - add owning handles so cert references don't leak in IsCertificateDistrustImminent r?jcj draft
authorDavid Keeler <dkeeler@mozilla.com>
Thu, 07 Dec 2017 15:08:43 -0800
changeset 709308 708304a7a8e3f55230c42935d4bd43f7ed1f8ff8
parent 709018 e30c06a1074c2455f47e0bafb9bb84ec1a09d682
child 743392 2b30e5bcf3c0142f5bebc3bd151eec71eefbee83
push id92612
push userbmo:dkeeler@mozilla.com
push dateThu, 07 Dec 2017 23:13:21 +0000
reviewersjcj
bugs1424085
milestone59.0a1
bug 1424085 - add owning handles so cert references don't leak in IsCertificateDistrustImminent r?jcj nsIX509Cert::GetCert() returns a CERTCertificate whose reference count has already been increased. Before this patch, when IsCertificateDistrustImminent called CertDNIsInList(rootCert->GetCert(), RootSymantecDNs) and CertDNIsInList(aCert->GetCert(), RootAppleAndGoogleDNs), the reference count on those certificates would never get a corresponding decrement, so we would keep those certificates alive until shut down. A reasonable and consistent solution is to introduce a UniqueCERTCertificate handle in each case to own the reference. The status of this fix can be verified by setting MOZ_LOG="pipnss:4", running Firefox, connecting to any host, and then shutting down. If an NSS resource reference has been leaked, "[Main Thread]: E/pipnss NSS SHUTDOWN FAILURE" will be in the console output. Otherwise, "[Main Thread]: D/pipnss NSS shutdown =====>> OK <<=====" will be in the console output. This patch also removes nsIX509CertList::DeleteCert because it would also leak a reference. Luckily, nothing was using it. This patch also clarifies the implementation of nsIX509CertList::AddCert by making the ownership transfers explicit. MozReview-Commit-ID: 2qHo3DmhTPz
security/manager/ssl/nsIX509CertList.idl
security/manager/ssl/nsNSSCallbacks.cpp
security/manager/ssl/nsNSSCertificate.cpp
--- a/security/manager/ssl/nsIX509CertList.idl
+++ b/security/manager/ssl/nsIX509CertList.idl
@@ -17,18 +17,16 @@ class nsNSSCertList;
 %}
 [ptr] native nsNSSCertListPtr(nsNSSCertList);
 
 [scriptable, builtinclass, uuid(ae74cda5-cd2f-473f-96f5-f0b7fff62c68)]
 interface nsIX509CertList : nsISupports {
   [must_use]
   void addCert(in nsIX509Cert cert);
   [must_use]
-  void deleteCert(in nsIX509Cert cert);
-  [must_use]
   nsISimpleEnumerator getEnumerator();
 
   /**
    * Returns the raw, backing cert list.
    * Must be called only from functions where an nsNSSShutDownPreventionLock
    * has been acquired.
    */
   [notxpcom, noscript, must_use]
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -1303,30 +1303,34 @@ IsCertificateDistrustImminent(nsIX509Cer
 
   // If the end entity's notBefore date is after 2016-06-01, this algorithm
   // doesn't apply, so exit false before we do any iterating
   if (notBefore >= JUNE_1_2016) {
     aResult = false;
     return NS_OK;
   }
 
+  // We need an owning handle when calling nsIX509Cert::GetCert().
+  UniqueCERTCertificate nssRootCert(rootCert->GetCert());
   // If the root is not one of the Symantec roots, exit false
-  if (!CertDNIsInList(rootCert->GetCert(), RootSymantecDNs)) {
+  if (!CertDNIsInList(nssRootCert.get(), RootSymantecDNs)) {
     aResult = false;
     return NS_OK;
   }
 
   // Look for one of the intermediates to be in the whitelist
   bool foundInWhitelist = false;
   RefPtr<nsNSSCertList> intCertList = intCerts->GetCertList();
 
   intCertList->ForEachCertificateInChain(
     [&foundInWhitelist] (nsCOMPtr<nsIX509Cert> aCert, bool aHasMore,
                          /* out */ bool& aContinue) {
-      if (CertDNIsInList(aCert->GetCert(), RootAppleAndGoogleDNs)) {
+      // We need an owning handle when calling nsIX509Cert::GetCert().
+      UniqueCERTCertificate nssCert(aCert->GetCert());
+      if (CertDNIsInList(nssCert.get(), RootAppleAndGoogleDNs)) {
         foundInWhitelist = true;
         aContinue = false;
       }
       return NS_OK;
   });
 
   // If this chain did not match the whitelist, exit true
   aResult = !foundInWhitelist;
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -1150,59 +1150,32 @@ nsNSSCertList::GetCertList()
 
 NS_IMETHODIMP
 nsNSSCertList::AddCert(nsIX509Cert* aCert)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-  CERTCertificate* cert = aCert->GetCert();
+  // We need an owning handle when calling nsIX509Cert::GetCert().
+  UniqueCERTCertificate cert(aCert->GetCert());
   if (!cert) {
     NS_ERROR("Somehow got nullptr for mCertificate in nsNSSCertificate.");
     return NS_ERROR_FAILURE;
   }
 
   if (!mCertList) {
     NS_ERROR("Somehow got nullptr for mCertList in nsNSSCertList.");
     return NS_ERROR_FAILURE;
   }
-  // XXX: check return value!
-  CERT_AddCertToListTail(mCertList.get(), cert);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNSSCertList::DeleteCert(nsIX509Cert* aCert)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
+  if (CERT_AddCertToListTail(mCertList.get(), cert.get()) != SECSuccess) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
-  CERTCertificate* cert = aCert->GetCert();
-  CERTCertListNode* node;
-
-  if (!cert) {
-    NS_ERROR("Somehow got nullptr for mCertificate in nsNSSCertificate.");
-    return NS_ERROR_FAILURE;
-  }
-
-  if (!mCertList) {
-    NS_ERROR("Somehow got nullptr for mCertList in nsNSSCertList.");
-    return NS_ERROR_FAILURE;
-  }
-
-  for (node = CERT_LIST_HEAD(mCertList.get());
-       !CERT_LIST_END(node, mCertList.get()); node = CERT_LIST_NEXT(node)) {
-    if (node->cert == cert) {
-	CERT_RemoveCertListNode(node);
-        return NS_OK;
-    }
-  }
-  return NS_OK; // XXX Should we fail if we couldn't find it?
+  Unused << cert.release(); // Ownership transferred to the cert list.
+  return NS_OK;
 }
 
 UniqueCERTCertList
 nsNSSCertList::DupCertList(const UniqueCERTCertList& certList,
                            const nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
   if (!certList) {
     return nullptr;