1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include <algorithm>
8 #include <vector>
9 
10 #include "IOInterposer.h"
11 
12 #include "IOInterposerPrivate.h"
13 #include "MainThreadIOLogger.h"
14 #include "mozilla/Atomics.h"
15 #include "mozilla/Mutex.h"
16 #include "mozilla/RefPtr.h"
17 #include "mozilla/StaticPtr.h"
18 #include "mozilla/ThreadLocal.h"
19 #include "nscore.h"  // for NS_FREE_PERMANENT_DATA
20 #if !defined(XP_WIN)
21 #include "NSPRInterposer.h"
22 #endif  // !defined(XP_WIN)
23 #include "nsXULAppAPI.h"
24 #include "PoisonIOInterposer.h"
25 
26 using namespace mozilla;
27 
28 namespace {
29 
30 /** Find if a vector contains a specific element */
31 template <class T>
VectorContains(const std::vector<T> & aVector,const T & aElement)32 bool VectorContains(const std::vector<T>& aVector, const T& aElement) {
33   return std::find(aVector.begin(), aVector.end(), aElement) != aVector.end();
34 }
35 
36 /** Remove element from a vector */
37 template <class T>
VectorRemove(std::vector<T> & aVector,const T & aElement)38 void VectorRemove(std::vector<T>& aVector, const T& aElement) {
39   typename std::vector<T>::iterator newEnd =
40       std::remove(aVector.begin(), aVector.end(), aElement);
41   aVector.erase(newEnd, aVector.end());
42 }
43 
44 /** Lists of Observers */
45 struct ObserverLists {
46  private:
~ObserverLists__anonab3aa38d0111::ObserverLists47   ~ObserverLists() {}
48 
49  public:
50   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObserverLists)
51 
ObserverLists__anonab3aa38d0111::ObserverLists52   ObserverLists() {}
53 
ObserverLists__anonab3aa38d0111::ObserverLists54   ObserverLists(ObserverLists const& aOther)
55       : mCreateObservers(aOther.mCreateObservers),
56         mReadObservers(aOther.mReadObservers),
57         mWriteObservers(aOther.mWriteObservers),
58         mFSyncObservers(aOther.mFSyncObservers),
59         mStatObservers(aOther.mStatObservers),
60         mCloseObservers(aOther.mCloseObservers),
61         mStageObservers(aOther.mStageObservers) {}
62   // Lists of observers for I/O events.
63   // These are implemented as vectors since they are allowed to survive gecko,
64   // without reporting leaks. This is necessary for the IOInterposer to be used
65   // for late-write checks.
66   std::vector<IOInterposeObserver*> mCreateObservers;
67   std::vector<IOInterposeObserver*> mReadObservers;
68   std::vector<IOInterposeObserver*> mWriteObservers;
69   std::vector<IOInterposeObserver*> mFSyncObservers;
70   std::vector<IOInterposeObserver*> mStatObservers;
71   std::vector<IOInterposeObserver*> mCloseObservers;
72   std::vector<IOInterposeObserver*> mStageObservers;
73 };
74 
75 class PerThreadData {
76  public:
PerThreadData(bool aIsMainThread=false)77   explicit PerThreadData(bool aIsMainThread = false)
78       : mIsMainThread(aIsMainThread),
79         mIsHandlingObservation(false),
80         mCurrentGeneration(0) {
81     MOZ_COUNT_CTOR(PerThreadData);
82   }
83 
~PerThreadData()84   ~PerThreadData() { MOZ_COUNT_DTOR(PerThreadData); }
85 
CallObservers(IOInterposeObserver::Observation & aObservation)86   void CallObservers(IOInterposeObserver::Observation& aObservation) {
87     // Prevent recursive reporting.
88     if (mIsHandlingObservation) {
89       return;
90     }
91 
92     mIsHandlingObservation = true;
93     // Decide which list of observers to inform
94     std::vector<IOInterposeObserver*>* observers = nullptr;
95     switch (aObservation.ObservedOperation()) {
96       case IOInterposeObserver::OpCreateOrOpen:
97         observers = &mObserverLists->mCreateObservers;
98         break;
99       case IOInterposeObserver::OpRead:
100         observers = &mObserverLists->mReadObservers;
101         break;
102       case IOInterposeObserver::OpWrite:
103         observers = &mObserverLists->mWriteObservers;
104         break;
105       case IOInterposeObserver::OpFSync:
106         observers = &mObserverLists->mFSyncObservers;
107         break;
108       case IOInterposeObserver::OpStat:
109         observers = &mObserverLists->mStatObservers;
110         break;
111       case IOInterposeObserver::OpClose:
112         observers = &mObserverLists->mCloseObservers;
113         break;
114       case IOInterposeObserver::OpNextStage:
115         observers = &mObserverLists->mStageObservers;
116         break;
117       default: {
118         // Invalid IO operation, see documentation comment for
119         // IOInterposer::Report()
120         MOZ_ASSERT(false);
121         // Just ignore it in non-debug builds.
122         return;
123       }
124     }
125     MOZ_ASSERT(observers);
126 
127     // Inform observers
128     for (auto i = observers->begin(), e = observers->end(); i != e; ++i) {
129       (*i)->Observe(aObservation);
130     }
131     mIsHandlingObservation = false;
132   }
133 
GetCurrentGeneration() const134   inline uint32_t GetCurrentGeneration() const { return mCurrentGeneration; }
135 
IsMainThread() const136   inline bool IsMainThread() const { return mIsMainThread; }
137 
SetObserverLists(uint32_t aNewGeneration,RefPtr<ObserverLists> & aNewLists)138   inline void SetObserverLists(uint32_t aNewGeneration,
139                                RefPtr<ObserverLists>& aNewLists) {
140     mCurrentGeneration = aNewGeneration;
141     mObserverLists = aNewLists;
142   }
143 
ClearObserverLists()144   inline void ClearObserverLists() {
145     if (mObserverLists) {
146       mCurrentGeneration = 0;
147       mObserverLists = nullptr;
148     }
149   }
150 
151  private:
152   bool mIsMainThread;
153   bool mIsHandlingObservation;
154   uint32_t mCurrentGeneration;
155   RefPtr<ObserverLists> mObserverLists;
156 };
157 
158 class MasterList {
159  public:
MasterList()160   MasterList()
161       : mObservedOperations(IOInterposeObserver::OpNone), mIsEnabled(true) {
162     MOZ_COUNT_CTOR(MasterList);
163   }
164 
~MasterList()165   ~MasterList() { MOZ_COUNT_DTOR(MasterList); }
166 
Disable()167   inline void Disable() { mIsEnabled = false; }
Enable()168   inline void Enable() { mIsEnabled = true; }
169 
Register(IOInterposeObserver::Operation aOp,IOInterposeObserver * aObserver)170   void Register(IOInterposeObserver::Operation aOp,
171                 IOInterposeObserver* aObserver) {
172     IOInterposer::AutoLock lock(mLock);
173 
174     ObserverLists* newLists = nullptr;
175     if (mObserverLists) {
176       newLists = new ObserverLists(*mObserverLists);
177     } else {
178       newLists = new ObserverLists();
179     }
180     // You can register to observe multiple types of observations
181     // but you'll never be registered twice for the same observations.
182     if (aOp & IOInterposeObserver::OpCreateOrOpen &&
183         !VectorContains(newLists->mCreateObservers, aObserver)) {
184       newLists->mCreateObservers.push_back(aObserver);
185     }
186     if (aOp & IOInterposeObserver::OpRead &&
187         !VectorContains(newLists->mReadObservers, aObserver)) {
188       newLists->mReadObservers.push_back(aObserver);
189     }
190     if (aOp & IOInterposeObserver::OpWrite &&
191         !VectorContains(newLists->mWriteObservers, aObserver)) {
192       newLists->mWriteObservers.push_back(aObserver);
193     }
194     if (aOp & IOInterposeObserver::OpFSync &&
195         !VectorContains(newLists->mFSyncObservers, aObserver)) {
196       newLists->mFSyncObservers.push_back(aObserver);
197     }
198     if (aOp & IOInterposeObserver::OpStat &&
199         !VectorContains(newLists->mStatObservers, aObserver)) {
200       newLists->mStatObservers.push_back(aObserver);
201     }
202     if (aOp & IOInterposeObserver::OpClose &&
203         !VectorContains(newLists->mCloseObservers, aObserver)) {
204       newLists->mCloseObservers.push_back(aObserver);
205     }
206     if (aOp & IOInterposeObserver::OpNextStage &&
207         !VectorContains(newLists->mStageObservers, aObserver)) {
208       newLists->mStageObservers.push_back(aObserver);
209     }
210     mObserverLists = newLists;
211     mObservedOperations =
212         (IOInterposeObserver::Operation)(mObservedOperations | aOp);
213 
214     mCurrentGeneration++;
215   }
216 
Unregister(IOInterposeObserver::Operation aOp,IOInterposeObserver * aObserver)217   void Unregister(IOInterposeObserver::Operation aOp,
218                   IOInterposeObserver* aObserver) {
219     IOInterposer::AutoLock lock(mLock);
220 
221     ObserverLists* newLists = nullptr;
222     if (mObserverLists) {
223       newLists = new ObserverLists(*mObserverLists);
224     } else {
225       newLists = new ObserverLists();
226     }
227 
228     if (aOp & IOInterposeObserver::OpCreateOrOpen) {
229       VectorRemove(newLists->mCreateObservers, aObserver);
230       if (newLists->mCreateObservers.empty()) {
231         mObservedOperations = (IOInterposeObserver::Operation)(
232             mObservedOperations & ~IOInterposeObserver::OpCreateOrOpen);
233       }
234     }
235     if (aOp & IOInterposeObserver::OpRead) {
236       VectorRemove(newLists->mReadObservers, aObserver);
237       if (newLists->mReadObservers.empty()) {
238         mObservedOperations = (IOInterposeObserver::Operation)(
239             mObservedOperations & ~IOInterposeObserver::OpRead);
240       }
241     }
242     if (aOp & IOInterposeObserver::OpWrite) {
243       VectorRemove(newLists->mWriteObservers, aObserver);
244       if (newLists->mWriteObservers.empty()) {
245         mObservedOperations = (IOInterposeObserver::Operation)(
246             mObservedOperations & ~IOInterposeObserver::OpWrite);
247       }
248     }
249     if (aOp & IOInterposeObserver::OpFSync) {
250       VectorRemove(newLists->mFSyncObservers, aObserver);
251       if (newLists->mFSyncObservers.empty()) {
252         mObservedOperations = (IOInterposeObserver::Operation)(
253             mObservedOperations & ~IOInterposeObserver::OpFSync);
254       }
255     }
256     if (aOp & IOInterposeObserver::OpStat) {
257       VectorRemove(newLists->mStatObservers, aObserver);
258       if (newLists->mStatObservers.empty()) {
259         mObservedOperations = (IOInterposeObserver::Operation)(
260             mObservedOperations & ~IOInterposeObserver::OpStat);
261       }
262     }
263     if (aOp & IOInterposeObserver::OpClose) {
264       VectorRemove(newLists->mCloseObservers, aObserver);
265       if (newLists->mCloseObservers.empty()) {
266         mObservedOperations = (IOInterposeObserver::Operation)(
267             mObservedOperations & ~IOInterposeObserver::OpClose);
268       }
269     }
270     if (aOp & IOInterposeObserver::OpNextStage) {
271       VectorRemove(newLists->mStageObservers, aObserver);
272       if (newLists->mStageObservers.empty()) {
273         mObservedOperations = (IOInterposeObserver::Operation)(
274             mObservedOperations & ~IOInterposeObserver::OpNextStage);
275       }
276     }
277     mObserverLists = newLists;
278     mCurrentGeneration++;
279   }
280 
Update(PerThreadData & aPtd)281   void Update(PerThreadData& aPtd) {
282     if (mCurrentGeneration == aPtd.GetCurrentGeneration()) {
283       return;
284     }
285     // If the generation counts don't match then we need to update the current
286     // thread's observer list with the new master list.
287     IOInterposer::AutoLock lock(mLock);
288     aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
289   }
290 
IsObservedOperation(IOInterposeObserver::Operation aOp)291   inline bool IsObservedOperation(IOInterposeObserver::Operation aOp) {
292     // The quick reader may observe that no locks are being employed here,
293     // hence the result of the operations is truly undefined. However, most
294     // computers will usually return either true or false, which is good enough.
295     // It is not a problem if we occasionally report more or less IO than is
296     // actually occurring.
297     return mIsEnabled && !!(mObservedOperations & aOp);
298   }
299 
300  private:
301   RefPtr<ObserverLists> mObserverLists;
302   // Note, we cannot use mozilla::Mutex here as the ObserverLists may be leaked
303   // (We want to monitor IO during shutdown). Furthermore, as we may have to
304   // unregister observers during shutdown an OffTheBooksMutex is not an option
305   // either, as its base calls into sDeadlockDetector which may be nullptr
306   // during shutdown.
307   IOInterposer::Mutex mLock;
308   // Flags tracking which operations are being observed
309   IOInterposeObserver::Operation mObservedOperations;
310   // Used for quickly disabling everything by IOInterposer::Disable()
311   Atomic<bool> mIsEnabled;
312   // Used to inform threads that the master observer list has changed
313   Atomic<uint32_t> mCurrentGeneration;
314 };
315 
316 // Special observation used by IOInterposer::EnteringNextStage()
317 class NextStageObservation : public IOInterposeObserver::Observation {
318  public:
NextStageObservation()319   NextStageObservation()
320       : IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage,
321                                          "IOInterposer", false) {
322     mStart = TimeStamp::Now();
323     mEnd = mStart;
324   }
325 };
326 
327 // List of observers registered
328 static StaticAutoPtr<MasterList> sMasterList;
329 static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData;
330 static bool sThreadLocalDataInitialized;
331 }  // namespace
332 
Observation(Operation aOperation,const char * aReference,bool aShouldReport)333 IOInterposeObserver::Observation::Observation(Operation aOperation,
334                                               const char* aReference,
335                                               bool aShouldReport)
336     : mOperation(aOperation),
337       mReference(aReference),
338       mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
339                     aShouldReport) {
340   if (mShouldReport) {
341     mStart = TimeStamp::Now();
342   }
343 }
344 
Observation(Operation aOperation,const TimeStamp & aStart,const TimeStamp & aEnd,const char * aReference)345 IOInterposeObserver::Observation::Observation(Operation aOperation,
346                                               const TimeStamp& aStart,
347                                               const TimeStamp& aEnd,
348                                               const char* aReference)
349     : mOperation(aOperation),
350       mStart(aStart),
351       mEnd(aEnd),
352       mReference(aReference),
353       mShouldReport(false) {}
354 
ObservedOperationString() const355 const char* IOInterposeObserver::Observation::ObservedOperationString() const {
356   switch (mOperation) {
357     case OpCreateOrOpen:
358       return "create/open";
359     case OpRead:
360       return "read";
361     case OpWrite:
362       return "write";
363     case OpFSync:
364       return "fsync";
365     case OpStat:
366       return "stat";
367     case OpClose:
368       return "close";
369     case OpNextStage:
370       return "NextStage";
371     default:
372       return "unknown";
373   }
374 }
375 
Report()376 void IOInterposeObserver::Observation::Report() {
377   if (mShouldReport) {
378     mEnd = TimeStamp::Now();
379     IOInterposer::Report(*this);
380   }
381 }
382 
Init()383 bool IOInterposer::Init() {
384   // Don't initialize twice...
385   if (sMasterList) {
386     return true;
387   }
388   if (!sThreadLocalData.init()) {
389     return false;
390   }
391   sThreadLocalDataInitialized = true;
392   bool isMainThread = true;
393   RegisterCurrentThread(isMainThread);
394   sMasterList = new MasterList();
395 
396   MainThreadIOLogger::Init();
397 
398   // Now we initialize the various interposers depending on platform
399   InitPoisonIOInterposer();
400   // We don't hook NSPR on Windows because PoisonIOInterposer captures a
401   // superset of the former's events.
402 #if !defined(XP_WIN)
403   InitNSPRIOInterposing();
404 #endif
405   return true;
406 }
407 
IsMainThread()408 bool IOInterposeObserver::IsMainThread() {
409   if (!sThreadLocalDataInitialized) {
410     return false;
411   }
412   PerThreadData* ptd = sThreadLocalData.get();
413   if (!ptd) {
414     return false;
415   }
416   return ptd->IsMainThread();
417 }
418 
Clear()419 void IOInterposer::Clear() {
420 /* Clear() is a no-op on release builds so that we may continue to trap I/O
421    until process termination. In leak-checking builds, we need to shut down
422    IOInterposer so that all references are properly released. */
423 #ifdef NS_FREE_PERMANENT_DATA
424   UnregisterCurrentThread();
425   sMasterList = nullptr;
426 #endif
427 }
428 
Disable()429 void IOInterposer::Disable() {
430   if (!sMasterList) {
431     return;
432   }
433   sMasterList->Disable();
434 }
435 
Enable()436 void IOInterposer::Enable() {
437   if (!sMasterList) {
438     return;
439   }
440   sMasterList->Enable();
441 }
442 
Report(IOInterposeObserver::Observation & aObservation)443 void IOInterposer::Report(IOInterposeObserver::Observation& aObservation) {
444   PerThreadData* ptd = sThreadLocalData.get();
445   if (!ptd) {
446     // In this case the current thread is not registered with IOInterposer.
447     // Alternatively we could take the slow path and just lock everything if
448     // we're not registered. That could potentially perform poorly, though.
449     return;
450   }
451 
452   if (!sMasterList) {
453     // If there is no longer a master list then we should clear the local one.
454     ptd->ClearObserverLists();
455     return;
456   }
457 
458   sMasterList->Update(*ptd);
459 
460   // Don't try to report if there's nobody listening.
461   if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) {
462     return;
463   }
464 
465   ptd->CallObservers(aObservation);
466 }
467 
IsObservedOperation(IOInterposeObserver::Operation aOp)468 bool IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp) {
469   return sMasterList && sMasterList->IsObservedOperation(aOp);
470 }
471 
Register(IOInterposeObserver::Operation aOp,IOInterposeObserver * aObserver)472 void IOInterposer::Register(IOInterposeObserver::Operation aOp,
473                             IOInterposeObserver* aObserver) {
474   MOZ_ASSERT(aObserver);
475   if (!sMasterList || !aObserver) {
476     return;
477   }
478 
479   sMasterList->Register(aOp, aObserver);
480 }
481 
Unregister(IOInterposeObserver::Operation aOp,IOInterposeObserver * aObserver)482 void IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
483                               IOInterposeObserver* aObserver) {
484   if (!sMasterList) {
485     return;
486   }
487 
488   sMasterList->Unregister(aOp, aObserver);
489 }
490 
RegisterCurrentThread(bool aIsMainThread)491 void IOInterposer::RegisterCurrentThread(bool aIsMainThread) {
492   if (!sThreadLocalDataInitialized) {
493     return;
494   }
495   MOZ_ASSERT(!sThreadLocalData.get());
496   PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
497   sThreadLocalData.set(curThreadData);
498 }
499 
UnregisterCurrentThread()500 void IOInterposer::UnregisterCurrentThread() {
501   if (!sThreadLocalDataInitialized) {
502     return;
503   }
504   PerThreadData* curThreadData = sThreadLocalData.get();
505   MOZ_ASSERT(curThreadData);
506   sThreadLocalData.set(nullptr);
507   delete curThreadData;
508 }
509 
EnteringNextStage()510 void IOInterposer::EnteringNextStage() {
511   if (!sMasterList) {
512     return;
513   }
514   NextStageObservation observation;
515   Report(observation);
516 }
517