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 : #ifndef nsNSSShutDown_h
6 : #define nsNSSShutDown_h
7 :
8 : #include "PLDHashTable.h"
9 : #include "mozilla/Assertions.h"
10 : #include "mozilla/CondVar.h"
11 : #include "mozilla/Mutex.h"
12 : #include "mozilla/StaticMutex.h"
13 : #include "nscore.h"
14 : #include "nspr.h"
15 :
16 : class nsNSSShutDownObject;
17 : class nsOnPK11LogoutCancelObject;
18 :
19 : // Singleton, owned by nsNSSShutDownList
20 : class nsNSSActivityState
21 : {
22 : public:
23 : nsNSSActivityState();
24 : ~nsNSSActivityState();
25 :
26 : // Call enter/leave when PSM enters a scope during which
27 : // shutting down NSS is prohibited.
28 : void enter();
29 : void leave();
30 : // Wait for all activity to stop, and block any other thread on entering
31 : // relevant PSM code.
32 : PRStatus restrictActivityToCurrentThread();
33 :
34 : // Go back to normal state.
35 : void releaseCurrentThreadActivityRestriction();
36 :
37 : private:
38 : // The lock protecting all our member variables.
39 : mozilla::Mutex mNSSActivityStateLock;
40 :
41 : // The activity variable, bound to our lock,
42 : // used either to signal the activity counter reaches zero,
43 : // or a thread restriction has been released.
44 : mozilla::CondVar mNSSActivityChanged;
45 :
46 : // The number of active scopes holding resources.
47 : int mNSSActivityCounter;
48 :
49 : // nullptr means "no restriction"
50 : // if not null, activity is only allowed on that thread
51 : PRThread* mNSSRestrictedThread;
52 : };
53 :
54 : // Helper class that automatically enters/leaves the global activity state
55 : class nsNSSShutDownPreventionLock
56 : {
57 : public:
58 : nsNSSShutDownPreventionLock();
59 : ~nsNSSShutDownPreventionLock();
60 : };
61 :
62 : // Singleton, used by nsNSSComponent to track the list of PSM objects,
63 : // which hold NSS resources and support the "early cleanup mechanism".
64 : class nsNSSShutDownList
65 : {
66 : public:
67 : // track instances that support early cleanup
68 : static void remember(nsNSSShutDownObject *o);
69 : static void forget(nsNSSShutDownObject *o);
70 :
71 : // track instances that would like notification when
72 : // a PK11 logout operation is performed.
73 : static void remember(nsOnPK11LogoutCancelObject *o);
74 : static void forget(nsOnPK11LogoutCancelObject *o);
75 :
76 : // Release all tracked NSS resources and prevent nsNSSShutDownObjects from
77 : // using NSS functions.
78 : static nsresult evaporateAllNSSResourcesAndShutDown();
79 :
80 : // PSM has been asked to log out of a token.
81 : // Notify all registered instances that want to react to that event.
82 : static nsresult doPK11Logout();
83 :
84 : // Signal entering/leaving a scope where shutting down NSS is prohibited.
85 : static void enterActivityState();
86 : static void leaveActivityState();
87 :
88 : private:
89 : static bool construct(const mozilla::StaticMutexAutoLock& /*proofOfLock*/);
90 :
91 : nsNSSShutDownList();
92 : ~nsNSSShutDownList();
93 :
94 : protected:
95 : PLDHashTable mObjects;
96 : PLDHashTable mPK11LogoutCancelObjects;
97 : nsNSSActivityState mActivityState;
98 : };
99 :
100 : /*
101 : A class deriving from nsNSSShutDownObject will have its instances
102 : automatically tracked in a list. However, it must follow some rules
103 : to assure correct behaviour.
104 :
105 : The tricky part is that it is not possible to call virtual
106 : functions from a destructor.
107 :
108 : The deriving class must override virtualDestroyNSSReference().
109 : Within this function, it should clean up all resources held to NSS.
110 : The function will be called by the global list, if it is time to
111 : shut down NSS before all references have been freed.
112 :
113 : The same code that goes into virtualDestroyNSSReference must
114 : also be called from the destructor of the deriving class,
115 : which is the standard cleanup (not called from the tracking list).
116 :
117 : Because of that duplication, it is suggested to implement a
118 : function destructorSafeDestroyNSSReference() in the deriving
119 : class, and make the implementation of virtualDestroyNSSReference()
120 : call destructorSafeDestroyNSSReference().
121 :
122 : The destructor of the derived class must prevent NSS shutdown on
123 : another thread by acquiring an nsNSSShutDownPreventionLock. It must
124 : then check to see if NSS has already been shut down by calling
125 : isAlreadyShutDown(). If NSS has not been shut down, the destructor
126 : must then call destructorSafeDestroyNSSReference() and then
127 : shutdown(ShutdownCalledFrom::Object). The second call will deregister with
128 : the tracking list, to ensure no additional attempt to free the resources
129 : will be made.
130 :
131 : ----------------------------------------------------------------------------
132 : IMPORTANT NOTE REGARDING CLASSES THAT IMPLEMENT nsNSSShutDownObject BUT DO
133 : NOT DIRECTLY HOLD NSS RESOURCES:
134 : ----------------------------------------------------------------------------
135 : Currently, classes that do not hold NSS resources but do call NSS functions
136 : inherit from nsNSSShutDownObject (and use the lock/isAlreadyShutDown
137 : mechanism) as a way of ensuring it is safe to call those functions. Because
138 : these classes do not hold any resources, however, it is tempting to skip the
139 : destructor component of this interface. This MUST NOT be done, because
140 : if an object of such a class is destructed before the nsNSSShutDownList
141 : processes all of its entries, this essentially causes a use-after-free when
142 : nsNSSShutDownList reaches the entry that has been destroyed. The safe way to
143 : do this is to implement the destructor as usual but omit the call to
144 : destructorSafeDestroyNSSReference() as it is unnecessary and probably isn't
145 : defined for that class.
146 :
147 : destructorSafeDestroyNSSReference() does not need to acquire an
148 : nsNSSShutDownPreventionLock or check isAlreadyShutDown() as long as it
149 : is only called by the destructor that has already acquired the lock and
150 : checked for shutdown or by the NSS shutdown code itself (which acquires
151 : the same lock and checks if objects it cleans up have already cleaned
152 : up themselves).
153 :
154 : destructorSafeDestroyNSSReference() MUST NOT cause any other
155 : nsNSSShutDownObject to be deconstructed. Doing so can cause
156 : unsupported concurrent operations on the hash table in the
157 : nsNSSShutDownList.
158 :
159 : class derivedClass : public nsISomeInterface,
160 : public nsNSSShutDownObject
161 : {
162 : virtual void virtualDestroyNSSReference()
163 : {
164 : destructorSafeDestroyNSSReference();
165 : }
166 :
167 : void destructorSafeDestroyNSSReference()
168 : {
169 : // clean up all NSS resources here
170 : }
171 :
172 : virtual ~derivedClass()
173 : {
174 : nsNSSShutDownPreventionLock locker;
175 : if (isAlreadyShutDown()) {
176 : return;
177 : }
178 : destructorSafeDestroyNSSReference();
179 : shutdown(ShutdownCalledFrom::Object);
180 : }
181 :
182 : NS_IMETHODIMP doSomething()
183 : {
184 : if (isAlreadyShutDown())
185 : return NS_ERROR_NOT_AVAILABLE;
186 :
187 : // use the NSS resources and do something
188 : }
189 : };
190 : */
191 :
192 : class nsNSSShutDownObject
193 : {
194 : public:
195 : enum class ShutdownCalledFrom {
196 : List,
197 : Object,
198 : };
199 :
200 17 : nsNSSShutDownObject()
201 17 : {
202 17 : mAlreadyShutDown = false;
203 17 : nsNSSShutDownList::remember(this);
204 17 : }
205 :
206 13 : virtual ~nsNSSShutDownObject()
207 13 : {
208 : // The derived class must call
209 : // shutdown(ShutdownCalledFrom::Object);
210 : // in its destructor
211 13 : }
212 :
213 13 : void shutdown(ShutdownCalledFrom calledFrom)
214 : {
215 13 : if (!mAlreadyShutDown) {
216 13 : switch (calledFrom) {
217 : case ShutdownCalledFrom::Object:
218 13 : nsNSSShutDownList::forget(this);
219 13 : break;
220 : case ShutdownCalledFrom::List:
221 0 : virtualDestroyNSSReference();
222 0 : break;
223 : default:
224 0 : MOZ_CRASH("shutdown() called from an unknown source");
225 : }
226 13 : mAlreadyShutDown = true;
227 : }
228 13 : }
229 :
230 : bool isAlreadyShutDown() const;
231 :
232 : protected:
233 : virtual void virtualDestroyNSSReference() = 0;
234 : private:
235 : volatile bool mAlreadyShutDown;
236 : };
237 :
238 : class nsOnPK11LogoutCancelObject
239 : {
240 : public:
241 0 : nsOnPK11LogoutCancelObject()
242 0 : : mIsLoggedOut(false)
243 : {
244 0 : nsNSSShutDownList::remember(this);
245 0 : }
246 :
247 0 : virtual ~nsOnPK11LogoutCancelObject()
248 0 : {
249 0 : nsNSSShutDownList::forget(this);
250 0 : }
251 :
252 0 : void logout()
253 : {
254 : // We do not care for a race condition.
255 : // Once the bool arrived at false,
256 : // later calls to isPK11LoggedOut() will see it.
257 : // This is a one-time change from 0 to 1.
258 0 : mIsLoggedOut = true;
259 0 : }
260 :
261 0 : bool isPK11LoggedOut()
262 : {
263 0 : return mIsLoggedOut;
264 : }
265 :
266 : private:
267 : volatile bool mIsLoggedOut;
268 : };
269 :
270 : #endif // nsNSSShutDown_h
|