gecko-dev/security/manager/ssl/nsNSSShutDown.cpp
Cykesiopka a16f7b0f6a Bug 1296316 - Convert nsNSSShutDownObject::CalledFromType to an enum class. r=mgoodwin
enum classes are in general safer than plain enums, and as such should be
preferred.

MozReview-Commit-ID: 1FK89SNhdk4

--HG--
extra : rebase_source : 764c4855026c02d8c9e33ca33637fec54ea5ca31
2016-08-20 23:00:19 +08:00

289 lines
7.2 KiB
C++

/* 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 "nsNSSShutDown.h"
#include "mozilla/Casting.h"
#include "nsCOMPtr.h"
using namespace mozilla;
extern LazyLogModule gPIPNSSLog;
struct ObjectHashEntry : PLDHashEntryHdr {
nsNSSShutDownObject *obj;
};
static bool
ObjectSetMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
{
const ObjectHashEntry *entry = static_cast<const ObjectHashEntry*>(hdr);
return entry->obj == static_cast<const nsNSSShutDownObject*>(key);
}
static void
ObjectSetInitEntry(PLDHashEntryHdr *hdr, const void *key)
{
ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr);
entry->obj = const_cast<nsNSSShutDownObject*>(static_cast<const nsNSSShutDownObject*>(key));
}
static const PLDHashTableOps gSetOps = {
PLDHashTable::HashVoidPtrKeyStub,
ObjectSetMatchEntry,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub,
ObjectSetInitEntry
};
StaticMutex sListLock;
Atomic<bool> sInShutdown(false);
nsNSSShutDownList *singleton = nullptr;
nsNSSShutDownList::nsNSSShutDownList()
: mObjects(&gSetOps, sizeof(ObjectHashEntry))
, mPK11LogoutCancelObjects(&gSetOps, sizeof(ObjectHashEntry))
{
}
nsNSSShutDownList::~nsNSSShutDownList()
{
PR_ASSERT(this == singleton);
singleton = nullptr;
}
void nsNSSShutDownList::remember(nsNSSShutDownObject *o)
{
StaticMutexAutoLock lock(sListLock);
if (!nsNSSShutDownList::construct(lock)) {
return;
}
PR_ASSERT(o);
singleton->mObjects.Add(o, fallible);
}
void nsNSSShutDownList::forget(nsNSSShutDownObject *o)
{
StaticMutexAutoLock lock(sListLock);
if (!singleton) {
return;
}
PR_ASSERT(o);
singleton->mObjects.Remove(o);
}
void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o)
{
StaticMutexAutoLock lock(sListLock);
if (!nsNSSShutDownList::construct(lock)) {
return;
}
PR_ASSERT(o);
singleton->mPK11LogoutCancelObjects.Add(o, fallible);
}
void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o)
{
StaticMutexAutoLock lock(sListLock);
if (!singleton) {
return;
}
PR_ASSERT(o);
singleton->mPK11LogoutCancelObjects.Remove(o);
}
nsresult nsNSSShutDownList::doPK11Logout()
{
StaticMutexAutoLock lock(sListLock);
if (!singleton) {
return NS_OK;
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("canceling all open SSL sockets to disallow future IO\n"));
// During our iteration we will set a bunch of PRBools to true.
// Nobody else ever modifies that bool, only we do.
// We only must ensure that our objects do not go away.
// This is guaranteed by holding the list lock.
for (auto iter = singleton->mPK11LogoutCancelObjects.Iter();
!iter.Done();
iter.Next()) {
auto entry = static_cast<ObjectHashEntry*>(iter.Get());
nsOnPK11LogoutCancelObject* pklco =
BitwiseCast<nsOnPK11LogoutCancelObject*, nsNSSShutDownObject*>(entry->obj);
if (pklco) {
pklco->logout();
}
}
return NS_OK;
}
nsresult nsNSSShutDownList::evaporateAllNSSResources()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
if (!NS_IsMainThread()) {
return NS_ERROR_NOT_SAME_THREAD;
}
StaticMutexAutoLock lock(sListLock);
// Other threads can acquire an nsNSSShutDownPreventionLock and cause this
// thread to block when it calls restructActivityToCurrentThread, below. If
// those other threads then attempt to create an object that must be
// remembered by the shut down list, they will call
// nsNSSShutDownList::remember, which attempts to acquire sListLock.
// Consequently, holding sListLock while we're in
// restrictActivityToCurrentThread would result in deadlock. sListLock
// protects the singleton, so if we enforce that the singleton only be created
// and destroyed on the main thread, and if we similarly enforce that this
// function is only called on the main thread, what we can do is check that
// the singleton hasn't already gone away and then we don't actually have to
// hold sListLock while calling restrictActivityToCurrentThread.
if (!singleton) {
return NS_OK;
}
{
StaticMutexAutoUnlock unlock(sListLock);
PRStatus rv = singleton->mActivityState.restrictActivityToCurrentThread();
if (rv != PR_SUCCESS) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("failed to restrict activity to current thread"));
return NS_ERROR_FAILURE;
}
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("now evaporating NSS resources"));
// Never free more than one entry, because other threads might be calling
// us and remove themselves while we are iterating over the list,
// and the behaviour of changing the list while iterating is undefined.
while (singleton) {
auto iter = singleton->mObjects.Iter();
if (iter.Done()) {
break;
}
auto entry = static_cast<ObjectHashEntry*>(iter.Get());
{
StaticMutexAutoUnlock unlock(sListLock);
entry->obj->shutdown(nsNSSShutDownObject::ShutdownCalledFrom::List);
}
iter.Remove();
}
if (!singleton) {
return NS_ERROR_FAILURE;
}
singleton->mActivityState.releaseCurrentThreadActivityRestriction();
return NS_OK;
}
void nsNSSShutDownList::enterActivityState()
{
StaticMutexAutoLock lock(sListLock);
if (nsNSSShutDownList::construct(lock)) {
singleton->mActivityState.enter();
}
}
void nsNSSShutDownList::leaveActivityState()
{
StaticMutexAutoLock lock(sListLock);
if (singleton) {
singleton->mActivityState.leave();
}
}
bool nsNSSShutDownList::construct(const StaticMutexAutoLock& /*proofOfLock*/)
{
if (!singleton && !sInShutdown && XRE_IsParentProcess()) {
singleton = new nsNSSShutDownList();
}
return !!singleton;
}
void nsNSSShutDownList::shutdown()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
StaticMutexAutoLock lock(sListLock);
sInShutdown = true;
if (singleton) {
delete singleton;
}
}
nsNSSActivityState::nsNSSActivityState()
:mNSSActivityStateLock("nsNSSActivityState.mNSSActivityStateLock"),
mNSSActivityChanged(mNSSActivityStateLock,
"nsNSSActivityState.mNSSActivityStateLock"),
mNSSActivityCounter(0),
mNSSRestrictedThread(nullptr)
{
}
nsNSSActivityState::~nsNSSActivityState()
{
}
void nsNSSActivityState::enter()
{
MutexAutoLock lock(mNSSActivityStateLock);
while (mNSSRestrictedThread && mNSSRestrictedThread != PR_GetCurrentThread()) {
mNSSActivityChanged.Wait();
}
++mNSSActivityCounter;
}
void nsNSSActivityState::leave()
{
MutexAutoLock lock(mNSSActivityStateLock);
--mNSSActivityCounter;
mNSSActivityChanged.NotifyAll();
}
PRStatus nsNSSActivityState::restrictActivityToCurrentThread()
{
MutexAutoLock lock(mNSSActivityStateLock);
while (mNSSActivityCounter > 0) {
mNSSActivityChanged.Wait(PR_TicksPerSecond());
}
mNSSRestrictedThread = PR_GetCurrentThread();
return PR_SUCCESS;
}
void nsNSSActivityState::releaseCurrentThreadActivityRestriction()
{
MutexAutoLock lock(mNSSActivityStateLock);
mNSSRestrictedThread = nullptr;
mNSSActivityChanged.NotifyAll();
}
nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock()
{
nsNSSShutDownList::enterActivityState();
}
nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock()
{
nsNSSShutDownList::leaveActivityState();
}