Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "nsNSSShutDown.h"
6 :
7 : #include "mozilla/Casting.h"
8 : #include "nsCOMPtr.h"
9 :
10 : using namespace mozilla;
11 :
12 : extern LazyLogModule gPIPNSSLog;
13 :
14 : struct ObjectHashEntry : PLDHashEntryHdr {
15 : nsNSSShutDownObject *obj;
16 : };
17 :
18 : static bool
19 13 : ObjectSetMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
20 : {
21 13 : const ObjectHashEntry *entry = static_cast<const ObjectHashEntry*>(hdr);
22 13 : return entry->obj == static_cast<const nsNSSShutDownObject*>(key);
23 : }
24 :
25 : static void
26 17 : ObjectSetInitEntry(PLDHashEntryHdr *hdr, const void *key)
27 : {
28 17 : ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr);
29 17 : entry->obj = const_cast<nsNSSShutDownObject*>(static_cast<const nsNSSShutDownObject*>(key));
30 17 : }
31 :
32 : static const PLDHashTableOps gSetOps = {
33 : PLDHashTable::HashVoidPtrKeyStub,
34 : ObjectSetMatchEntry,
35 : PLDHashTable::MoveEntryStub,
36 : PLDHashTable::ClearEntryStub,
37 : ObjectSetInitEntry
38 : };
39 :
40 3 : StaticMutex sListLock;
41 : Atomic<bool> sInShutdown(false);
42 : nsNSSShutDownList *singleton = nullptr;
43 :
44 1 : nsNSSShutDownList::nsNSSShutDownList()
45 : : mObjects(&gSetOps, sizeof(ObjectHashEntry))
46 1 : , mPK11LogoutCancelObjects(&gSetOps, sizeof(ObjectHashEntry))
47 : {
48 1 : }
49 :
50 0 : nsNSSShutDownList::~nsNSSShutDownList()
51 : {
52 0 : MOZ_ASSERT(this == singleton);
53 0 : MOZ_ASSERT(sInShutdown,
54 : "evaporateAllNSSResourcesAndShutDown() should have been called");
55 0 : singleton = nullptr;
56 0 : }
57 :
58 17 : void nsNSSShutDownList::remember(nsNSSShutDownObject *o)
59 : {
60 34 : StaticMutexAutoLock lock(sListLock);
61 17 : if (!nsNSSShutDownList::construct(lock)) {
62 0 : return;
63 : }
64 :
65 17 : MOZ_ASSERT(o);
66 17 : singleton->mObjects.Add(o, fallible);
67 : }
68 :
69 13 : void nsNSSShutDownList::forget(nsNSSShutDownObject *o)
70 : {
71 26 : StaticMutexAutoLock lock(sListLock);
72 13 : if (!singleton) {
73 0 : return;
74 : }
75 :
76 13 : MOZ_ASSERT(o);
77 13 : singleton->mObjects.Remove(o);
78 : }
79 :
80 0 : void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o)
81 : {
82 0 : StaticMutexAutoLock lock(sListLock);
83 0 : if (!nsNSSShutDownList::construct(lock)) {
84 0 : return;
85 : }
86 :
87 0 : MOZ_ASSERT(o);
88 0 : singleton->mPK11LogoutCancelObjects.Add(o, fallible);
89 : }
90 :
91 0 : void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o)
92 : {
93 0 : StaticMutexAutoLock lock(sListLock);
94 0 : if (!singleton) {
95 0 : return;
96 : }
97 :
98 0 : MOZ_ASSERT(o);
99 0 : singleton->mPK11LogoutCancelObjects.Remove(o);
100 : }
101 :
102 0 : nsresult nsNSSShutDownList::doPK11Logout()
103 : {
104 0 : StaticMutexAutoLock lock(sListLock);
105 0 : if (!singleton) {
106 0 : return NS_OK;
107 : }
108 :
109 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
110 : ("canceling all open SSL sockets to disallow future IO\n"));
111 :
112 : // During our iteration we will set a bunch of PRBools to true.
113 : // Nobody else ever modifies that bool, only we do.
114 : // We only must ensure that our objects do not go away.
115 : // This is guaranteed by holding the list lock.
116 :
117 0 : for (auto iter = singleton->mPK11LogoutCancelObjects.Iter();
118 0 : !iter.Done();
119 0 : iter.Next()) {
120 0 : auto entry = static_cast<ObjectHashEntry*>(iter.Get());
121 : nsOnPK11LogoutCancelObject* pklco =
122 0 : BitwiseCast<nsOnPK11LogoutCancelObject*, nsNSSShutDownObject*>(entry->obj);
123 0 : if (pklco) {
124 0 : pklco->logout();
125 : }
126 : }
127 :
128 0 : return NS_OK;
129 : }
130 :
131 0 : nsresult nsNSSShutDownList::evaporateAllNSSResourcesAndShutDown()
132 : {
133 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
134 0 : if (!NS_IsMainThread()) {
135 0 : return NS_ERROR_NOT_SAME_THREAD;
136 : }
137 :
138 0 : StaticMutexAutoLock lock(sListLock);
139 : // Other threads can acquire an nsNSSShutDownPreventionLock and cause this
140 : // thread to block when it calls restructActivityToCurrentThread, below. If
141 : // those other threads then attempt to create an object that must be
142 : // remembered by the shut down list, they will call
143 : // nsNSSShutDownList::remember, which attempts to acquire sListLock.
144 : // Consequently, holding sListLock while we're in
145 : // restrictActivityToCurrentThread would result in deadlock. sListLock
146 : // protects the singleton, so if we enforce that the singleton only be created
147 : // and destroyed on the main thread, and if we similarly enforce that this
148 : // function is only called on the main thread, what we can do is check that
149 : // the singleton hasn't already gone away and then we don't actually have to
150 : // hold sListLock while calling restrictActivityToCurrentThread.
151 0 : if (!singleton) {
152 0 : return NS_OK;
153 : }
154 :
155 0 : sInShutdown = true;
156 :
157 : {
158 0 : StaticMutexAutoUnlock unlock(sListLock);
159 0 : PRStatus rv = singleton->mActivityState.restrictActivityToCurrentThread();
160 0 : if (rv != PR_SUCCESS) {
161 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
162 : ("failed to restrict activity to current thread"));
163 0 : return NS_ERROR_FAILURE;
164 : }
165 : }
166 :
167 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("now evaporating NSS resources"));
168 :
169 : // Never free more than one entry, because other threads might be calling
170 : // us and remove themselves while we are iterating over the list,
171 : // and the behaviour of changing the list while iterating is undefined.
172 0 : while (singleton) {
173 0 : auto iter = singleton->mObjects.Iter();
174 0 : if (iter.Done()) {
175 0 : break;
176 : }
177 0 : auto entry = static_cast<ObjectHashEntry*>(iter.Get());
178 : {
179 0 : StaticMutexAutoUnlock unlock(sListLock);
180 0 : entry->obj->shutdown(nsNSSShutDownObject::ShutdownCalledFrom::List);
181 : }
182 0 : iter.Remove();
183 : }
184 :
185 0 : if (!singleton) {
186 0 : return NS_ERROR_FAILURE;
187 : }
188 :
189 0 : singleton->mActivityState.releaseCurrentThreadActivityRestriction();
190 0 : delete singleton;
191 :
192 0 : return NS_OK;
193 : }
194 :
195 277 : void nsNSSShutDownList::enterActivityState()
196 : {
197 554 : StaticMutexAutoLock lock(sListLock);
198 277 : if (nsNSSShutDownList::construct(lock)) {
199 277 : singleton->mActivityState.enter();
200 : }
201 277 : }
202 :
203 277 : void nsNSSShutDownList::leaveActivityState()
204 : {
205 554 : StaticMutexAutoLock lock(sListLock);
206 277 : if (singleton) {
207 277 : singleton->mActivityState.leave();
208 : }
209 277 : }
210 :
211 294 : bool nsNSSShutDownList::construct(const StaticMutexAutoLock& /*proofOfLock*/)
212 : {
213 294 : if (!singleton && !sInShutdown && XRE_IsParentProcess()) {
214 1 : singleton = new nsNSSShutDownList();
215 : }
216 :
217 294 : return !!singleton;
218 : }
219 :
220 1 : nsNSSActivityState::nsNSSActivityState()
221 : :mNSSActivityStateLock("nsNSSActivityState.mNSSActivityStateLock"),
222 : mNSSActivityChanged(mNSSActivityStateLock,
223 : "nsNSSActivityState.mNSSActivityStateLock"),
224 : mNSSActivityCounter(0),
225 1 : mNSSRestrictedThread(nullptr)
226 : {
227 1 : }
228 :
229 0 : nsNSSActivityState::~nsNSSActivityState()
230 : {
231 0 : }
232 :
233 277 : void nsNSSActivityState::enter()
234 : {
235 554 : MutexAutoLock lock(mNSSActivityStateLock);
236 :
237 277 : while (mNSSRestrictedThread && mNSSRestrictedThread != PR_GetCurrentThread()) {
238 0 : mNSSActivityChanged.Wait();
239 : }
240 :
241 277 : ++mNSSActivityCounter;
242 277 : }
243 :
244 277 : void nsNSSActivityState::leave()
245 : {
246 554 : MutexAutoLock lock(mNSSActivityStateLock);
247 :
248 277 : --mNSSActivityCounter;
249 :
250 277 : mNSSActivityChanged.NotifyAll();
251 277 : }
252 :
253 0 : PRStatus nsNSSActivityState::restrictActivityToCurrentThread()
254 : {
255 0 : MutexAutoLock lock(mNSSActivityStateLock);
256 :
257 0 : while (mNSSActivityCounter > 0) {
258 0 : mNSSActivityChanged.Wait(PR_TicksPerSecond());
259 : }
260 :
261 0 : mNSSRestrictedThread = PR_GetCurrentThread();
262 :
263 0 : return PR_SUCCESS;
264 : }
265 :
266 0 : void nsNSSActivityState::releaseCurrentThreadActivityRestriction()
267 : {
268 0 : MutexAutoLock lock(mNSSActivityStateLock);
269 :
270 0 : mNSSRestrictedThread = nullptr;
271 :
272 0 : mNSSActivityChanged.NotifyAll();
273 0 : }
274 :
275 277 : nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock()
276 : {
277 277 : nsNSSShutDownList::enterActivityState();
278 277 : }
279 :
280 277 : nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock()
281 : {
282 277 : nsNSSShutDownList::leaveActivityState();
283 277 : }
284 :
285 : bool
286 277 : nsNSSShutDownObject::isAlreadyShutDown() const
287 : {
288 277 : return mAlreadyShutDown || sInShutdown;
289 : }
|