bug 1460312 - cancel the timeout timer in OCSP request implementation r?jcj draft
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 09 May 2018 10:06:49 -0700
changeset 793195 9573afbf191a16e601324fe0257521e37a5a36a0
parent 793056 9294f67b3f3bd4a3dd898961148cecd8bfc1ce9c
push id109310
push userbmo:dkeeler@mozilla.com
push dateWed, 09 May 2018 17:07:10 +0000
reviewersjcj
bugs1460312, 1456489
milestone62.0a1
bug 1460312 - cancel the timeout timer in OCSP request implementation r?jcj Bug 1456489 cleaned up our OCSP request implementation a bit. One simplification it made was to not cancel the timeout timer. It turns out that if we don't, the OCSPRequest that constitutes the timeout callback's closure might not be valid if the request has completed (because the timer doesn't own a strong reference to it). The fix is simple: cancel the timer when the request completes. Note that we don't have to do the reverse because necko has a strong reference to the request. MozReview-Commit-ID: 2WHFLAcGBAw
security/manager/ssl/nsNSSCallbacks.cpp
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -91,19 +91,21 @@ private:
   // On the main thread, a channel is set up to perform the request. This gets
   // dispatched to necko. At the same time, a timeout timer is initialized. If
   // the necko request completes, the response data is filled out, mNotifiedDone
   // is set to true, and the monitor is notified. The original thread then wakes
   // up and continues with the results that have been filled out. If the request
   // times out, again the response data is filled out, mNotifiedDone is set to
   // true, and the monitor is notified. The first of these two events wins. That
   // is, if the timeout timer fires but the request completes shortly after, the
-  // caller will see the request as having timed out, and vice-versa. (Also note
-  // that no effort is made to cancel either the request or the timeout timer if
-  // the other event completes first.)
+  // caller will see the request as having timed out.
+  // When the request completes (i.e. OnStreamComplete runs), the timer will be
+  // cancelled. This is how we know the closure in OnTimeout is valid. If the
+  // timer fires before OnStreamComplete runs, it should be safe to not cancel
+  // the request because necko has a strong reference to it.
   Monitor mMonitor;
   bool mNotifiedDone;
   nsCOMPtr<nsIStreamLoader> mLoader;
   const nsCString mAIALocation;
   const OriginAttributes mOriginAttributes;
   const Vector<uint8_t> mPOSTData;
   const TimeDuration mTimeout;
   nsCOMPtr<nsITimer> mTimeoutTimer;
@@ -356,16 +358,19 @@ OCSPRequest::NotifyDone(nsresult rv, Mon
     return NS_ERROR_FAILURE;
   }
 
   if (mNotifiedDone) {
     return mResponseResult;
   }
   mLoader = nullptr;
   mResponseResult = rv;
+  if (mTimeoutTimer) {
+    Unused << mTimeoutTimer->Cancel();
+  }
   mNotifiedDone = true;
   lock.Notify();
   return rv;
 }
 
 NS_IMETHODIMP
 OCSPRequest::OnStreamComplete(nsIStreamLoader* aLoader,
                               nsISupports* aContext,
@@ -425,16 +430,19 @@ OCSPRequest::OnStreamComplete(nsIStreamL
 void
 OCSPRequest::OnTimeout(nsITimer* timer, void* closure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!NS_IsMainThread()) {
     return;
   }
 
+  // We know the OCSPRequest is still alive because if the request had completed
+  // (i.e. OnStreamComplete ran), the timer would have been cancelled in
+  // NotifyDone.
   OCSPRequest* self = static_cast<OCSPRequest*>(closure);
   MonitorAutoLock lock(self->mMonitor);
   self->mTimeoutTimer = nullptr;
   self->NotifyDone(NS_ERROR_NET_TIMEOUT, lock);
 }
 
 mozilla::pkix::Result
 DoOCSPRequest(const nsCString& aiaLocation,