bug 420187, hang in nsNSSHttpRequestSession::internal_send_receive_attempt r=rrelyea, pending more comments blocking1.9=shaver approval1.9b5=beltzner

This commit is contained in:
kaie@kuix.de 2008-03-25 12:34:48 -07:00
Родитель bfdb68b9a2
Коммит cfd263a72b
8 изменённых файлов: 209 добавлений и 75 удалений

Просмотреть файл

@ -162,6 +162,7 @@ nsSecureBrowserUIImpl::nsSecureBrowserUIImpl()
mMonitor = PR_NewMonitor();
mOnStateLocationChangeReentranceDetection = 0;
mTransferringRequests.ops = nsnull;
mInconsistency = PR_FALSE;
mNewToplevelSecurityState = STATE_IS_INSECURE;
mNewToplevelIsEV = PR_FALSE;
mNewToplevelSecurityStateKnown = PR_TRUE;
@ -596,10 +597,6 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress,
PRUint32 aProgressStateFlags,
nsresult aStatus)
{
nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
"unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
/*
All discussion, unless otherwise mentioned, only refers to
http, https, file or wyciwig requests.
@ -690,19 +687,43 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress,
regardless of whether the load flags indicate a top level document.
*/
nsCOMPtr<nsIDOMWindow> windowForProgress;
aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
if (mOnStateLocationChangeReentranceDetection > 1)
{
nsAutoMonitor lock(mMonitor);
mInconsistency = PR_TRUE;
// we ignore all events until the reentrance is gone
return NS_ERROR_FAILURE;
}
PRBool mustResetAfterInconsistency = PR_FALSE;
nsCOMPtr<nsIDOMWindow> windowForProgress;
nsCOMPtr<nsIDOMWindow> window;
PRBool isViewSource;
{
nsAutoMonitor lock(mMonitor);
if (mInconsistency) {
mInconsistency = PR_FALSE;
mustResetAfterInconsistency = PR_TRUE;
}
window = do_QueryReferent(mWindow);
NS_ASSERTION(window, "Window has gone away?!");
isViewSource = mIsViewSource;
}
if (mustResetAfterInconsistency) {
mNewToplevelSecurityState = STATE_IS_INSECURE;
mNewToplevelIsEV = PR_FALSE;
mNewToplevelSecurityStateKnown = PR_TRUE;
mSSLStatus = nsnull;
ResetStateTracking();
}
aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
const PRBool isToplevelProgress = (windowForProgress.get() == window.get());
#ifdef PR_LOGGING
@ -1251,8 +1272,8 @@ void nsSecureBrowserUIImpl::UpdateMyFlags(PRBool &showWarning, lockIconState &wa
mNotifiedToplevelIsEV = mNewToplevelIsEV;
}
nsresult nsSecureBrowserUIImpl::TellTheWorld(PRBool &showWarning,
lockIconState &warnSecurityState,
nsresult nsSecureBrowserUIImpl::TellTheWorld(PRBool showWarning,
lockIconState warnSecurityState,
nsIRequest* aRequest)
{
nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
@ -1319,8 +1340,20 @@ nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
nsIURI* aLocation)
{
nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
"unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
if (mOnStateLocationChangeReentranceDetection > 1)
{
nsAutoMonitor lock(mMonitor);
mInconsistency = PR_TRUE;
// We ignore all events until the reentrance is gone
// and has been reset by ::OnStateChange.
return NS_ERROR_FAILURE;
}
// We could test for mInconsistency right here and exit,
// but let's avoid another lock to mMonitor.
// We'll do the preparation work based on the parameters,
// and once we are ready to lock the monitor, we'll do
// the inconsistency check.
PRBool updateIsViewSource = PR_FALSE;
PRBool temp_IsViewSource = PR_FALSE;
@ -1344,6 +1377,9 @@ nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
{
nsAutoMonitor lock(mMonitor);
if (mInconsistency) {
return NS_ERROR_FAILURE;
}
if (updateIsViewSource) {
mIsViewSource = temp_IsViewSource;
}

Просмотреть файл

@ -114,6 +114,7 @@ protected:
void ResetStateTracking();
PRUint32 mNewToplevelSecurityState;
PRPackedBool mInconsistency;
PRPackedBool mNewToplevelIsEV;
PRPackedBool mNewToplevelSecurityStateKnown;
PRPackedBool mIsViewSource;
@ -129,8 +130,8 @@ protected:
static nsresult MapInternalToExternalState(PRUint32* aState, lockIconState lock, PRBool ev);
nsresult UpdateSecurityState(nsIRequest* aRequest);
void UpdateMyFlags(PRBool &showWarning, lockIconState &warnSecurityState);
nsresult TellTheWorld(PRBool &showWarning,
lockIconState &warnSecurityState,
nsresult TellTheWorld(PRBool showWarning,
lockIconState warnSecurityState,
nsIRequest* aRequest);
nsresult EvaluateAndUpdateSecurityState(nsIRequest *aRequest);

Просмотреть файл

@ -80,20 +80,35 @@ NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
extern PRLogModuleInfo* gPIPNSSLog;
#endif
struct nsHTTPDownloadEvent : nsRunnable {
class nsHTTPDownloadEvent : public nsRunnable {
public:
nsHTTPDownloadEvent();
~nsHTTPDownloadEvent();
NS_IMETHOD Run();
void Cancel();
nsNSSHttpRequestSession *mRequestSession; // no ownership
nsCOMPtr<nsHTTPListener> mListener;
PRBool mResponsibleForDoneSignal;
protected:
PRLock *mLock;
// no nsCOMPtr. When I use it, I get assertions about
// loadgroup not being thread safe.
// So, let's use a raw pointer and ensure we only create and destroy
// it on the network thread ourselves.
nsILoadGroup *mLoadGroup;
PRThread *mLoadGroupOwnerThread;
};
nsHTTPDownloadEvent::nsHTTPDownloadEvent()
:mResponsibleForDoneSignal(PR_TRUE)
,mLock(nsnull)
,mLoadGroup(nsnull)
,mLoadGroupOwnerThread(nsnull)
{
}
@ -101,11 +116,17 @@ nsHTTPDownloadEvent::~nsHTTPDownloadEvent()
{
if (mResponsibleForDoneSignal && mListener)
mListener->send_done_signal();
if (mLock)
PR_DestroyLock(mLock);
}
NS_IMETHODIMP
nsHTTPDownloadEvent::Run()
{
mLock = PR_NewLock();
if (!mLock)
return NS_ERROR_OUT_OF_MEMORY;
if (!mListener)
return NS_OK;
@ -120,9 +141,16 @@ nsHTTPDownloadEvent::Run()
// Create a loadgroup for this new channel. This way if the channel
// is redirected, we'll have a way to cancel the resulting channel.
nsCOMPtr<nsILoadGroup> loadGroup =
do_CreateInstance(NS_LOADGROUP_CONTRACTID);
chan->SetLoadGroup(loadGroup);
nsCOMPtr<nsILoadGroup> lg;
{
nsAutoLock locker(mLock);
lg = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
mLoadGroup = lg.get();
NS_ADDREF(mLoadGroup);
mLoadGroupOwnerThread = PR_GetCurrentThread();
}
chan->SetLoadGroup(lg);
lg = nsnull;
if (mRequestSession->mHasPostData)
{
@ -148,8 +176,6 @@ nsHTTPDownloadEvent::Run()
rv = hchan->SetRequestMethod(mRequestSession->mRequestMethod);
NS_ENSURE_SUCCESS(rv, rv);
nsSSLThread::rememberPendingHTTPRequest(loadGroup);
mResponsibleForDoneSignal = PR_FALSE;
mListener->mResponsibleForDoneSignal = PR_TRUE;
@ -162,20 +188,47 @@ nsHTTPDownloadEvent::Run()
if (NS_FAILED(rv)) {
mListener->mResponsibleForDoneSignal = PR_FALSE;
mResponsibleForDoneSignal = PR_TRUE;
nsSSLThread::rememberPendingHTTPRequest(nsnull);
}
return NS_OK;
}
struct nsCancelHTTPDownloadEvent : nsRunnable {
nsRefPtr<nsHTTPDownloadEvent> mEvent;
NS_IMETHOD Run() {
nsSSLThread::cancelPendingHTTPRequest();
mEvent->Cancel();
mEvent = nsnull;
return NS_OK;
}
};
void
nsHTTPDownloadEvent::Cancel()
{
nsILoadGroup *lg = nsnull;
if (mLock) {
nsAutoLock locker(mLock);
if (mLoadGroup) {
if (mLoadGroupOwnerThread != PR_GetCurrentThread()) {
NS_ASSERTION(PR_FALSE,
"attempt to access nsHTTPDownloadEvent::mLoadGroup on multiple threads");
}
else {
lg = mLoadGroup;
mLoadGroup = nsnull;
}
}
}
if (lg) {
lg->Cancel(NS_ERROR_ABORT);
NS_RELEASE(lg);
}
}
SECStatus nsNSSHttpServerSession::createSessionFcn(const char *host,
PRUint16 portnum,
SEC_HTTP_SERVER_SESSION *pSession)
@ -367,7 +420,6 @@ nsNSSHttpRequestSession::internal_send_receive_attempt(PRBool &retryable_error,
}
PRBool request_canceled = PR_FALSE;
PRBool aborted_wait = PR_FALSE;
{
nsAutoLock locker(waitLock);
@ -410,30 +462,26 @@ nsNSSHttpRequestSession::internal_send_receive_attempt(PRBool &retryable_error,
if (!request_canceled)
{
if ((PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval)
PRBool wantExit = nsSSLThread::exitRequested();
PRBool timeout =
(PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval;
if (wantExit || timeout)
{
request_canceled = PR_TRUE;
// but we'll to continue to wait for waitFlag
nsCOMPtr<nsIRunnable> cancelevent = new nsCancelHTTPDownloadEvent;
nsRefPtr<nsCancelHTTPDownloadEvent> cancelevent = new nsCancelHTTPDownloadEvent;
cancelevent->mEvent = event;
rv = NS_DispatchToMainThread(cancelevent);
if (NS_FAILED(rv))
{
if (NS_FAILED(rv)) {
NS_WARNING("cannot post cancel event");
aborted_wait = PR_TRUE;
break;
}
break;
}
}
}
}
if (aborted_wait)
{
// we couldn't cancel it, let's no longer reference it
nsSSLThread::rememberPendingHTTPRequest(nsnull);
}
if (request_canceled)
return SECFailure;
@ -628,8 +676,6 @@ nsHTTPListener::OnStreamComplete(nsIStreamLoader* aLoader,
void nsHTTPListener::send_done_signal()
{
nsSSLThread::rememberPendingHTTPRequest(nsnull);
mResponsibleForDoneSignal = PR_FALSE;
{

Просмотреть файл

@ -1722,6 +1722,19 @@ nsNSSComponent::Init()
return rv;
}
// Access our string bundles now, this prevents assertions from I/O
// - nsStandardURL not thread-safe
// - wrong thread: 'NS_IsMainThread()' in nsIOService.cpp
// when loading error strings on the SSL threads.
{
NS_NAMED_LITERAL_STRING(dummy_name, "dummy");
nsXPIDLString result;
mPIPNSSBundle->GetStringFromName(dummy_name.get(),
getter_Copies(result));
mNSSErrorsBundle->GetStringFromName(dummy_name.get(),
getter_Copies(result));
}
if (!mPrefBranch) {
mPrefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
NS_ASSERTION(mPrefBranch, "Unable to get pref service");

Просмотреть файл

@ -204,6 +204,7 @@ nsNSSSocketInfo::nsNSSSocketInfo()
: mFd(nsnull),
mBlockingState(blocking_state_unknown),
mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE),
mDocShellDependentStuffKnown(PR_FALSE),
mExternalErrorReporting(PR_FALSE),
mForSTARTTLS(PR_FALSE),
mHandshakePending(PR_TRUE),
@ -319,15 +320,30 @@ nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
return NS_OK;
}
mCallbacks = aCallbacks;
mDocShellDependentStuffKnown = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsNSSSocketInfo::EnsureDocShellDependentStuffKnown()
{
if (mDocShellDependentStuffKnown)
return NS_OK;
if (!mCallbacks || nsSSLThread::exitRequested())
return NS_ERROR_FAILURE;
mDocShellDependentStuffKnown = PR_TRUE;
nsCOMPtr<nsIInterfaceRequestor> proxiedCallbacks;
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIInterfaceRequestor),
static_cast<nsIInterfaceRequestor*>(aCallbacks),
static_cast<nsIInterfaceRequestor*>(mCallbacks),
NS_PROXY_SYNC,
getter_AddRefs(proxiedCallbacks));
mCallbacks = proxiedCallbacks;
// Are we running within a context that wants external SSL error reporting?
// We'll look at the presence of a security UI object inside docshell.
// If the docshell wants the lock icon, you'll get the ssl error pages, too.
@ -339,7 +355,7 @@ nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
nsCOMPtr<nsIDocShell> docshell;
nsCOMPtr<nsIDocShellTreeItem> item(do_GetInterface(mCallbacks));
nsCOMPtr<nsIDocShellTreeItem> item(do_GetInterface(proxiedCallbacks));
if (item)
{
nsCOMPtr<nsIDocShellTreeItem> proxiedItem;
@ -395,6 +411,8 @@ nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
nsresult
nsNSSSocketInfo::GetExternalErrorReporting(PRBool* state)
{
nsresult rv = EnsureDocShellDependentStuffKnown();
NS_ENSURE_SUCCESS(rv, rv);
*state = mExternalErrorReporting;
return NS_OK;
}
@ -465,10 +483,17 @@ NS_IMETHODIMP nsNSSSocketInfo::GetInterface(const nsIID & uuid, void * *result)
rv = ir->GetInterface(uuid, result);
} else {
// Proxy of the channel callbacks should probably go here, rather
// than in the password callback code
if (nsSSLThread::exitRequested())
return NS_ERROR_FAILURE;
rv = mCallbacks->GetInterface(uuid, result);
nsCOMPtr<nsIInterfaceRequestor> proxiedCallbacks;
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIInterfaceRequestor),
mCallbacks,
NS_PROXY_SYNC,
getter_AddRefs(proxiedCallbacks));
rv = proxiedCallbacks->GetInterface(uuid, result);
}
return rv;
}
@ -619,6 +644,8 @@ nsresult nsNSSSocketInfo::SetFileDescPtr(PRFileDesc* aFilePtr)
nsresult nsNSSSocketInfo::GetPreviousCert(nsIX509Cert** _result)
{
NS_ENSURE_ARG_POINTER(_result);
nsresult rv = EnsureDocShellDependentStuffKnown();
NS_ENSURE_SUCCESS(rv, rv);
*_result = mPreviousCert;
NS_IF_ADDREF(*_result);
@ -1139,6 +1166,9 @@ displayAlert(nsAFlatString &formattedString, nsNSSSocketInfo *infoObject)
// The interface requestor object may not be safe, so proxy the call to get
// the nsIPrompt.
if (nsSSLThread::exitRequested())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIInterfaceRequestor> proxiedCallbacks;
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIInterfaceRequestor),
@ -1173,6 +1203,10 @@ nsHandleSSLError(nsNSSSocketInfo *socketInfo, PRInt32 err)
return NS_OK;
}
if (nsSSLThread::exitRequested()) {
return NS_ERROR_FAILURE;
}
nsresult rv;
NS_DEFINE_CID(nssComponentCID, NS_NSSCOMPONENT_CID);
nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(nssComponentCID, &rv));
@ -1187,9 +1221,16 @@ nsHandleSSLError(nsNSSSocketInfo *socketInfo, PRInt32 err)
socketInfo->GetPort(&port);
// Try to get a nsISSLErrorListener implementation from the socket consumer.
nsCOMPtr<nsIInterfaceRequestor> callbacks;
socketInfo->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsIInterfaceRequestor> cb;
socketInfo->GetNotificationCallbacks(getter_AddRefs(cb));
if (cb) {
nsCOMPtr<nsIInterfaceRequestor> callbacks;
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIInterfaceRequestor),
cb,
NS_PROXY_SYNC,
getter_AddRefs(callbacks));
nsCOMPtr<nsISSLErrorListener> sel = do_GetInterface(callbacks);
if (sel) {
nsISSLErrorListener *proxy_sel = nsnull;
@ -2743,6 +2784,9 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
if (!infoObject)
return SECFailure;
if (nsSSLThread::exitRequested())
return cancel_and_failure(infoObject);
CERTCertificate *peerCert = nsnull;
CERTCertificateCleaner peerCertCleaner(peerCert);
peerCert = SSL_PeerCertificate(sslSocket);
@ -2911,9 +2955,16 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
nsresult rv;
// Try to get a nsIBadCertListener2 implementation from the socket consumer.
nsCOMPtr<nsIInterfaceRequestor> callbacks;
infoObject->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsIInterfaceRequestor> cb;
infoObject->GetNotificationCallbacks(getter_AddRefs(cb));
if (cb) {
nsCOMPtr<nsIInterfaceRequestor> callbacks;
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIInterfaceRequestor),
cb,
NS_PROXY_SYNC,
getter_AddRefs(callbacks));
nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(callbacks);
if (bcl) {
nsIBadCertListener2 *proxy_bcl = nsnull;

Просмотреть файл

@ -198,14 +198,15 @@ protected:
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
PRFileDesc* mFd;
nsCOMPtr<nsIX509Cert> mCert;
nsCOMPtr<nsIX509Cert> mPreviousCert;
nsCOMPtr<nsIX509Cert> mPreviousCert; // DocShellDependent
enum {
blocking_state_unknown, is_nonblocking_socket, is_blocking_socket
} mBlockingState;
PRUint32 mSecurityState;
nsString mShortDesc;
nsString mErrorMessage;
PRPackedBool mExternalErrorReporting;
PRPackedBool mDocShellDependentStuffKnown;
PRPackedBool mExternalErrorReporting; // DocShellDependent
PRPackedBool mForSTARTTLS;
PRPackedBool mHandshakePending;
PRPackedBool mCanceled;
@ -223,6 +224,8 @@ protected:
nsSSLSocketThreadData *mThreadData;
nsresult EnsureDocShellDependentStuffKnown();
private:
virtual void virtualDestroyNSSReference();
void destructorSafeDestroyNSSReference();

Просмотреть файл

@ -1120,28 +1120,14 @@ void nsSSLThread::Run(void)
}
}
void nsSSLThread::rememberPendingHTTPRequest(nsIRequest *aRequest)
PRBool nsSSLThread::exitRequested()
{
if (!ssl_thread_singleton)
return;
return PR_FALSE;
nsAutoLock threadLock(ssl_thread_singleton->mMutex);
// no lock
ssl_thread_singleton->mPendingHTTPRequest = aRequest;
}
void nsSSLThread::cancelPendingHTTPRequest()
{
if (!ssl_thread_singleton)
return;
nsAutoLock threadLock(ssl_thread_singleton->mMutex);
if (ssl_thread_singleton->mPendingHTTPRequest)
{
ssl_thread_singleton->mPendingHTTPRequest->Cancel(NS_ERROR_ABORT);
ssl_thread_singleton->mPendingHTTPRequest = nsnull;
}
return ssl_thread_singleton->mExitRequested;
}
nsSSLThread *nsSSLThread::ssl_thread_singleton = nsnull;

Просмотреть файл

@ -152,9 +152,7 @@ public:
static nsresult requestActivateSSL(nsNSSSocketInfo *si);
// Called from either Necko or SSL thread.
static void rememberPendingHTTPRequest(nsIRequest *aRequest);
static void cancelPendingHTTPRequest();
static PRBool exitRequested();
};
#endif //_NSSSLTHREAD_H_