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 #define INITGUID
8 
9 #include "mozilla/dom/ContentChild.h"
10 #include "mozilla/Move.h"
11 #include "mozilla/mscom/DispatchForwarder.h"
12 #include "mozilla/mscom/FastMarshaler.h"
13 #include "mozilla/mscom/Interceptor.h"
14 #include "mozilla/mscom/InterceptorLog.h"
15 #include "mozilla/mscom/MainThreadInvoker.h"
16 #include "mozilla/mscom/Objref.h"
17 #include "mozilla/mscom/Registration.h"
18 #include "mozilla/mscom/Utils.h"
19 #include "mozilla/ThreadLocal.h"
20 #include "MainThreadUtils.h"
21 #include "mozilla/Assertions.h"
22 #include "mozilla/DebugOnly.h"
23 #include "nsDirectoryServiceDefs.h"
24 #include "nsDirectoryServiceUtils.h"
25 #include "nsExceptionHandler.h"
26 #include "nsPrintfCString.h"
27 #include "nsRefPtrHashtable.h"
28 #include "nsThreadUtils.h"
29 #include "nsXULAppAPI.h"
30 
31 #define ENSURE_HR_SUCCEEDED(hr)       \
32   MOZ_ASSERT(SUCCEEDED((HRESULT)hr)); \
33   if (FAILED((HRESULT)hr)) {          \
34     return hr;                        \
35   }
36 
37 namespace mozilla {
38 namespace mscom {
39 namespace detail {
40 
41 class LiveSet final {
42  public:
LiveSet()43   LiveSet() : mMutex("mozilla::mscom::LiveSet::mMutex") {}
44 
Lock()45   void Lock() { mMutex.Lock(); }
46 
Unlock()47   void Unlock() { mMutex.Unlock(); }
48 
Put(IUnknown * aKey,already_AddRefed<IWeakReference> aValue)49   void Put(IUnknown* aKey, already_AddRefed<IWeakReference> aValue) {
50     mMutex.AssertCurrentThreadOwns();
51     mLiveSet.Put(aKey, Move(aValue));
52   }
53 
Get(IUnknown * aKey)54   RefPtr<IWeakReference> Get(IUnknown* aKey) {
55     mMutex.AssertCurrentThreadOwns();
56     RefPtr<IWeakReference> result;
57     mLiveSet.Get(aKey, getter_AddRefs(result));
58     return result;
59   }
60 
Remove(IUnknown * aKey)61   void Remove(IUnknown* aKey) {
62     mMutex.AssertCurrentThreadOwns();
63     mLiveSet.Remove(aKey);
64   }
65 
66  private:
67   Mutex mMutex;
68   nsRefPtrHashtable<nsPtrHashKey<IUnknown>, IWeakReference> mLiveSet;
69 };
70 
71 /**
72  * We don't use the normal XPCOM BaseAutoLock because we need the ability
73  * to explicitly Unlock.
74  */
75 class MOZ_RAII LiveSetAutoLock final {
76  public:
LiveSetAutoLock(LiveSet & aLiveSet)77   explicit LiveSetAutoLock(LiveSet& aLiveSet) : mLiveSet(&aLiveSet) {
78     aLiveSet.Lock();
79   }
80 
~LiveSetAutoLock()81   ~LiveSetAutoLock() {
82     if (mLiveSet) {
83       mLiveSet->Unlock();
84     }
85   }
86 
Unlock()87   void Unlock() {
88     MOZ_ASSERT(mLiveSet);
89     if (mLiveSet) {
90       mLiveSet->Unlock();
91       mLiveSet = nullptr;
92     }
93   }
94 
95   LiveSetAutoLock(const LiveSetAutoLock& aOther) = delete;
96   LiveSetAutoLock(LiveSetAutoLock&& aOther) = delete;
97   LiveSetAutoLock& operator=(const LiveSetAutoLock& aOther) = delete;
98   LiveSetAutoLock& operator=(LiveSetAutoLock&& aOther) = delete;
99 
100  private:
101   LiveSet* mLiveSet;
102 };
103 
104 class MOZ_RAII ReentrySentinel final {
105  public:
ReentrySentinel(Interceptor * aCurrent)106   explicit ReentrySentinel(Interceptor* aCurrent) : mCurInterceptor(aCurrent) {
107     static const bool kHasTls = tlsSentinelStackTop.init();
108     MOZ_RELEASE_ASSERT(kHasTls);
109 
110     mPrevSentinel = tlsSentinelStackTop.get();
111     tlsSentinelStackTop.set(this);
112   }
113 
~ReentrySentinel()114   ~ReentrySentinel() { tlsSentinelStackTop.set(mPrevSentinel); }
115 
IsOutermost() const116   bool IsOutermost() const {
117     return !(mPrevSentinel && mPrevSentinel->IsMarshaling(mCurInterceptor));
118   }
119 
120   ReentrySentinel(const ReentrySentinel&) = delete;
121   ReentrySentinel(ReentrySentinel&&) = delete;
122   ReentrySentinel& operator=(const ReentrySentinel&) = delete;
123   ReentrySentinel& operator=(ReentrySentinel&&) = delete;
124 
125  private:
IsMarshaling(Interceptor * aTopInterceptor) const126   bool IsMarshaling(Interceptor* aTopInterceptor) const {
127     return aTopInterceptor == mCurInterceptor ||
128            (mPrevSentinel && mPrevSentinel->IsMarshaling(aTopInterceptor));
129   }
130 
131  private:
132   Interceptor* mCurInterceptor;
133   ReentrySentinel* mPrevSentinel;
134 
135   static MOZ_THREAD_LOCAL(ReentrySentinel*) tlsSentinelStackTop;
136 };
137 
138 MOZ_THREAD_LOCAL(ReentrySentinel*) ReentrySentinel::tlsSentinelStackTop;
139 
140 class MOZ_RAII LoggedQIResult final {
141  public:
LoggedQIResult(REFIID aIid)142   explicit LoggedQIResult(REFIID aIid)
143       : mIid(aIid),
144         mHr(E_UNEXPECTED),
145         mTarget(nullptr),
146         mInterceptor(nullptr),
147         mBegin(TimeStamp::Now()) {}
148 
~LoggedQIResult()149   ~LoggedQIResult() {
150     if (!mTarget) {
151       return;
152     }
153 
154     TimeStamp end(TimeStamp::Now());
155     TimeDuration total(end - mBegin);
156     TimeDuration overhead(total - mNonOverheadDuration);
157 
158     InterceptorLog::QI(mHr, mTarget, mIid, mInterceptor, &overhead,
159                        &mNonOverheadDuration);
160   }
161 
Log(IUnknown * aTarget,IUnknown * aInterceptor)162   void Log(IUnknown* aTarget, IUnknown* aInterceptor) {
163     mTarget = aTarget;
164     mInterceptor = aInterceptor;
165   }
166 
operator =(HRESULT aHr)167   void operator=(HRESULT aHr) { mHr = aHr; }
168 
operator HRESULT()169   operator HRESULT() { return mHr; }
170 
operator TimeDuration*()171   operator TimeDuration*() { return &mNonOverheadDuration; }
172 
173   LoggedQIResult(const LoggedQIResult&) = delete;
174   LoggedQIResult(LoggedQIResult&&) = delete;
175   LoggedQIResult& operator=(const LoggedQIResult&) = delete;
176   LoggedQIResult& operator=(LoggedQIResult&&) = delete;
177 
178  private:
179   REFIID mIid;
180   HRESULT mHr;
181   IUnknown* mTarget;
182   IUnknown* mInterceptor;
183   TimeDuration mNonOverheadDuration;
184   TimeStamp mBegin;
185 };
186 
187 }  // namespace detail
188 
GetLiveSet()189 static detail::LiveSet& GetLiveSet() {
190   static detail::LiveSet sLiveSet;
191   return sLiveSet;
192 }
193 
194 MOZ_THREAD_LOCAL(bool) Interceptor::tlsCreatingStdMarshal;
195 
Create(STAUniquePtr<IUnknown> aTarget,IInterceptorSink * aSink,REFIID aInitialIid,void ** aOutInterface)196 /* static */ HRESULT Interceptor::Create(STAUniquePtr<IUnknown> aTarget,
197                                          IInterceptorSink* aSink,
198                                          REFIID aInitialIid,
199                                          void** aOutInterface) {
200   MOZ_ASSERT(aOutInterface && aTarget && aSink);
201   if (!aOutInterface) {
202     return E_INVALIDARG;
203   }
204 
205   detail::LiveSetAutoLock lock(GetLiveSet());
206 
207   RefPtr<IWeakReference> existingWeak(Move(GetLiveSet().Get(aTarget.get())));
208   if (existingWeak) {
209     RefPtr<IWeakReferenceSource> existingStrong;
210     if (SUCCEEDED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
211       // QI on existingStrong may touch other threads. Since we now hold a
212       // strong ref on the interceptor, we may now release the lock.
213       lock.Unlock();
214       return existingStrong->QueryInterface(aInitialIid, aOutInterface);
215     }
216   }
217 
218   *aOutInterface = nullptr;
219 
220   if (!aTarget || !aSink) {
221     return E_INVALIDARG;
222   }
223 
224   RefPtr<Interceptor> intcpt(new Interceptor(aSink));
225   return intcpt->GetInitialInterceptorForIID(lock, aInitialIid, Move(aTarget),
226                                              aOutInterface);
227 }
228 
Interceptor(IInterceptorSink * aSink)229 Interceptor::Interceptor(IInterceptorSink* aSink)
230     : WeakReferenceSupport(WeakReferenceSupport::Flags::eDestroyOnMainThread),
231       mEventSink(aSink),
232       mInterceptorMapMutex("mozilla::mscom::Interceptor::mInterceptorMapMutex"),
233       mStdMarshalMutex("mozilla::mscom::Interceptor::mStdMarshalMutex"),
234       mStdMarshal(nullptr) {
235   static const bool kHasTls = tlsCreatingStdMarshal.init();
236   MOZ_ASSERT(kHasTls);
237 
238   MOZ_ASSERT(aSink);
239   RefPtr<IWeakReference> weakRef;
240   if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef)))) {
241     aSink->SetInterceptor(weakRef);
242   }
243 }
244 
~Interceptor()245 Interceptor::~Interceptor() {
246   {  // Scope for lock
247     detail::LiveSetAutoLock lock(GetLiveSet());
248     GetLiveSet().Remove(mTarget.get());
249   }
250 
251   // This needs to run on the main thread because it releases target interface
252   // reference counts which may not be thread-safe.
253   MOZ_ASSERT(NS_IsMainThread());
254   for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len;
255        ++index) {
256     MapEntry& entry = mInterceptorMap[index];
257     entry.mInterceptor = nullptr;
258     entry.mTargetInterface->Release();
259   }
260 }
261 
262 HRESULT
GetClassForHandler(DWORD aDestContext,void * aDestContextPtr,CLSID * aHandlerClsid)263 Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
264                                 CLSID* aHandlerClsid) {
265   if (aDestContextPtr || !aHandlerClsid ||
266       aDestContext == MSHCTX_DIFFERENTMACHINE) {
267     return E_INVALIDARG;
268   }
269 
270   MOZ_ASSERT(mEventSink);
271   return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
272 }
273 
274 REFIID
MarshalAs(REFIID aIid) const275 Interceptor::MarshalAs(REFIID aIid) const {
276 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
277   return IsCallerExternalProcess() ? aIid : mEventSink->MarshalAs(aIid);
278 #else
279   return mEventSink->MarshalAs(aIid);
280 #endif  // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
281 }
282 
283 HRESULT
GetUnmarshalClass(REFIID riid,void * pv,DWORD dwDestContext,void * pvDestContext,DWORD mshlflags,CLSID * pCid)284 Interceptor::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
285                                void* pvDestContext, DWORD mshlflags,
286                                CLSID* pCid) {
287   return mStdMarshal->GetUnmarshalClass(MarshalAs(riid), pv, dwDestContext,
288                                         pvDestContext, mshlflags, pCid);
289 }
290 
291 HRESULT
GetMarshalSizeMax(REFIID riid,void * pv,DWORD dwDestContext,void * pvDestContext,DWORD mshlflags,DWORD * pSize)292 Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
293                                void* pvDestContext, DWORD mshlflags,
294                                DWORD* pSize) {
295   detail::ReentrySentinel sentinel(this);
296 
297   HRESULT hr = mStdMarshal->GetMarshalSizeMax(
298       MarshalAs(riid), pv, dwDestContext, pvDestContext, mshlflags, pSize);
299   if (FAILED(hr) || !sentinel.IsOutermost()) {
300     return hr;
301   }
302 
303   DWORD payloadSize = 0;
304   hr = mEventSink->GetHandlerPayloadSize(WrapNotNull(this),
305                                          WrapNotNull(&payloadSize));
306   if (hr == E_NOTIMPL) {
307     return S_OK;
308   }
309 
310   if (SUCCEEDED(hr)) {
311     *pSize += payloadSize;
312   }
313   return hr;
314 }
315 
316 HRESULT
MarshalInterface(IStream * pStm,REFIID riid,void * pv,DWORD dwDestContext,void * pvDestContext,DWORD mshlflags)317 Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
318                               DWORD dwDestContext, void* pvDestContext,
319                               DWORD mshlflags) {
320   detail::ReentrySentinel sentinel(this);
321 
322   HRESULT hr;
323 
324 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
325   // Save the current stream position
326   LARGE_INTEGER seekTo;
327   seekTo.QuadPart = 0;
328 
329   ULARGE_INTEGER objrefPos;
330 
331   hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
332   if (FAILED(hr)) {
333     return hr;
334   }
335 
336 #endif  // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
337 
338   hr = mStdMarshal->MarshalInterface(pStm, MarshalAs(riid), pv, dwDestContext,
339                                      pvDestContext, mshlflags);
340   if (FAILED(hr) || !sentinel.IsOutermost()) {
341     return hr;
342   }
343 
344 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
345   if (XRE_IsContentProcess() && IsCallerExternalProcess()) {
346     // The caller isn't our chrome process, so do not provide a handler.
347 
348     // First, save the current position that marks the current end of the
349     // OBJREF in the stream.
350     ULARGE_INTEGER endPos;
351     hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &endPos);
352     if (FAILED(hr)) {
353       return hr;
354     }
355 
356     // Now strip out the handler.
357     if (!StripHandlerFromOBJREF(WrapNotNull(pStm), objrefPos.QuadPart,
358                                 endPos.QuadPart)) {
359       return E_FAIL;
360     }
361 
362     return S_OK;
363   }
364 #endif  // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
365 
366   hr = mEventSink->WriteHandlerPayload(WrapNotNull(this), WrapNotNull(pStm));
367   if (hr == E_NOTIMPL) {
368     return S_OK;
369   }
370 
371   return hr;
372 }
373 
374 HRESULT
UnmarshalInterface(IStream * pStm,REFIID riid,void ** ppv)375 Interceptor::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
376   return mStdMarshal->UnmarshalInterface(pStm, riid, ppv);
377 }
378 
379 HRESULT
ReleaseMarshalData(IStream * pStm)380 Interceptor::ReleaseMarshalData(IStream* pStm) {
381   return mStdMarshal->ReleaseMarshalData(pStm);
382 }
383 
384 HRESULT
DisconnectObject(DWORD dwReserved)385 Interceptor::DisconnectObject(DWORD dwReserved) {
386   mEventSink->DisconnectHandlerRemotes();
387   return mStdMarshal->DisconnectObject(dwReserved);
388 }
389 
Lookup(REFIID aIid)390 Interceptor::MapEntry* Interceptor::Lookup(REFIID aIid) {
391   mInterceptorMapMutex.AssertCurrentThreadOwns();
392 
393   for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len;
394        ++index) {
395     if (mInterceptorMap[index].mIID == aIid) {
396       return &mInterceptorMap[index];
397     }
398   }
399   return nullptr;
400 }
401 
402 HRESULT
GetTargetForIID(REFIID aIid,InterceptorTargetPtr<IUnknown> & aTarget)403 Interceptor::GetTargetForIID(REFIID aIid,
404                              InterceptorTargetPtr<IUnknown>& aTarget) {
405   MutexAutoLock lock(mInterceptorMapMutex);
406   MapEntry* entry = Lookup(aIid);
407   if (entry) {
408     aTarget.reset(entry->mTargetInterface);
409     return S_OK;
410   }
411 
412   return E_NOINTERFACE;
413 }
414 
415 // CoGetInterceptor requires type metadata to be able to generate its emulated
416 // vtable. If no registered metadata is available, CoGetInterceptor returns
417 // kFileNotFound.
418 static const HRESULT kFileNotFound = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
419 
420 HRESULT
CreateInterceptor(REFIID aIid,IUnknown * aOuter,IUnknown ** aOutput)421 Interceptor::CreateInterceptor(REFIID aIid, IUnknown* aOuter,
422                                IUnknown** aOutput) {
423   // In order to aggregate, we *must* request IID_IUnknown as the initial
424   // interface for the interceptor, as that IUnknown is non-delegating.
425   // This is a fundamental rule for creating aggregated objects in COM.
426   HRESULT hr = ::CoGetInterceptor(aIid, aOuter, IID_IUnknown, (void**)aOutput);
427   if (hr != kFileNotFound) {
428     return hr;
429   }
430 
431   // In the case that CoGetInterceptor returns kFileNotFound, we can try to
432   // explicitly load typelib data from our runtime registration facility and
433   // pass that into CoGetInterceptorFromTypeInfo.
434 
435   RefPtr<ITypeInfo> typeInfo;
436   bool found = RegisteredProxy::Find(aIid, getter_AddRefs(typeInfo));
437   // If this assert fires then we have omitted registering the typelib for a
438   // required interface. To fix this, review our calls to mscom::RegisterProxy
439   // and mscom::RegisterTypelib, and add the additional typelib as necessary.
440   MOZ_ASSERT(found);
441   if (!found) {
442     return kFileNotFound;
443   }
444 
445   hr = ::CoGetInterceptorFromTypeInfo(aIid, aOuter, typeInfo, IID_IUnknown,
446                                       (void**)aOutput);
447   // If this assert fires then the interceptor doesn't like something about
448   // the format of the typelib. One thing in particular that it doesn't like
449   // is complex types that contain unions.
450   MOZ_ASSERT(SUCCEEDED(hr));
451   return hr;
452 }
453 
454 HRESULT
PublishTarget(detail::LiveSetAutoLock & aLiveSetLock,RefPtr<IUnknown> aInterceptor,REFIID aTargetIid,STAUniquePtr<IUnknown> aTarget)455 Interceptor::PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
456                            RefPtr<IUnknown> aInterceptor, REFIID aTargetIid,
457                            STAUniquePtr<IUnknown> aTarget) {
458   RefPtr<IWeakReference> weakRef;
459   HRESULT hr = GetWeakReference(getter_AddRefs(weakRef));
460   if (FAILED(hr)) {
461     return hr;
462   }
463 
464   // mTarget is a weak reference to aTarget. This is safe because we transfer
465   // ownership of aTarget into mInterceptorMap which remains live for the
466   // lifetime of this Interceptor.
467   mTarget = ToInterceptorTargetPtr(aTarget);
468   GetLiveSet().Put(mTarget.get(), weakRef.forget());
469 
470   // Now we transfer aTarget's ownership into mInterceptorMap.
471   mInterceptorMap.AppendElement(
472       MapEntry(aTargetIid, aInterceptor, aTarget.release()));
473 
474   // Release the live set lock because subsequent operations may post work to
475   // the main thread, creating potential for deadlocks.
476   aLiveSetLock.Unlock();
477   return S_OK;
478 }
479 
480 HRESULT
GetInitialInterceptorForIID(detail::LiveSetAutoLock & aLiveSetLock,REFIID aTargetIid,STAUniquePtr<IUnknown> aTarget,void ** aOutInterceptor)481 Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
482                                          REFIID aTargetIid,
483                                          STAUniquePtr<IUnknown> aTarget,
484                                          void** aOutInterceptor) {
485   MOZ_ASSERT(aOutInterceptor);
486   MOZ_ASSERT(aTargetIid != IID_IMarshal);
487   MOZ_ASSERT(!IsProxy(aTarget.get()));
488 
489   HRESULT hr = E_UNEXPECTED;
490 
491   auto hasFailed = [&hr]() -> bool { return FAILED(hr); };
492 
493   auto cleanup = [&aLiveSetLock]() -> void { aLiveSetLock.Unlock(); };
494 
495   ExecuteWhen<decltype(hasFailed), decltype(cleanup)> onFail(hasFailed,
496                                                              cleanup);
497 
498   if (aTargetIid == IID_IUnknown) {
499     // We must lock mInterceptorMapMutex so that nothing can race with us once
500     // we have been published to the live set.
501     MutexAutoLock lock(mInterceptorMapMutex);
502 
503     hr = PublishTarget(aLiveSetLock, nullptr, aTargetIid, Move(aTarget));
504     ENSURE_HR_SUCCEEDED(hr);
505 
506     hr = QueryInterface(aTargetIid, aOutInterceptor);
507     ENSURE_HR_SUCCEEDED(hr);
508     return hr;
509   }
510 
511   // Raise the refcount for stabilization purposes during aggregation
512   WeakReferenceSupport::StabilizeRefCount stabilizer(*this);
513 
514   RefPtr<IUnknown> unkInterceptor;
515   hr = CreateInterceptor(aTargetIid, static_cast<WeakReferenceSupport*>(this),
516                          getter_AddRefs(unkInterceptor));
517   ENSURE_HR_SUCCEEDED(hr);
518 
519   RefPtr<ICallInterceptor> interceptor;
520   hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
521                                       getter_AddRefs(interceptor));
522   ENSURE_HR_SUCCEEDED(hr);
523 
524   hr = interceptor->RegisterSink(mEventSink);
525   ENSURE_HR_SUCCEEDED(hr);
526 
527   // We must lock mInterceptorMapMutex so that nothing can race with us once we
528   // have been published to the live set.
529   MutexAutoLock lock(mInterceptorMapMutex);
530 
531   hr = PublishTarget(aLiveSetLock, unkInterceptor, aTargetIid, Move(aTarget));
532   ENSURE_HR_SUCCEEDED(hr);
533 
534   if (MarshalAs(aTargetIid) == aTargetIid) {
535     hr = unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor);
536     ENSURE_HR_SUCCEEDED(hr);
537     return hr;
538   }
539 
540   hr = GetInterceptorForIID(aTargetIid, aOutInterceptor, &lock);
541   ENSURE_HR_SUCCEEDED(hr);
542   return hr;
543 }
544 
545 HRESULT
GetInterceptorForIID(REFIID aIid,void ** aOutInterceptor)546 Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor) {
547   return GetInterceptorForIID(aIid, aOutInterceptor, nullptr);
548 }
549 
550 /**
551  * This method contains the core guts of the handling of QueryInterface calls
552  * that are delegated to us from the ICallInterceptor.
553  *
554  * @param aIid ID of the desired interface
555  * @param aOutInterceptor The resulting emulated vtable that corresponds to
556  * the interface specified by aIid.
557  * @param aAlreadyLocked Proof of an existing lock on |mInterceptorMapMutex|,
558  *                       if present.
559  */
560 HRESULT
GetInterceptorForIID(REFIID aIid,void ** aOutInterceptor,MutexAutoLock * aAlreadyLocked)561 Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor,
562                                   MutexAutoLock* aAlreadyLocked) {
563   detail::LoggedQIResult result(aIid);
564 
565   if (!aOutInterceptor) {
566     return E_INVALIDARG;
567   }
568 
569   if (aIid == IID_IUnknown) {
570     // Special case: When we see IUnknown, we just provide a reference to this
571     RefPtr<IInterceptor> intcpt(this);
572     intcpt.forget(aOutInterceptor);
573     return S_OK;
574   }
575 
576   REFIID interceptorIid = MarshalAs(aIid);
577 
578   RefPtr<IUnknown> unkInterceptor;
579   IUnknown* interfaceForQILog = nullptr;
580 
581   // (1) Check to see if we already have an existing interceptor for
582   // interceptorIid.
583   auto doLookup = [&]() -> void {
584     MapEntry* entry = Lookup(interceptorIid);
585     if (entry) {
586       unkInterceptor = entry->mInterceptor;
587       interfaceForQILog = entry->mTargetInterface;
588     }
589   };
590 
591   if (aAlreadyLocked) {
592     doLookup();
593   } else {
594     MutexAutoLock lock(mInterceptorMapMutex);
595     doLookup();
596   }
597 
598   // (1a) A COM interceptor already exists for this interface, so all we need
599   // to do is run a QI on it.
600   if (unkInterceptor) {
601     // Technically we didn't actually execute a QI on the target interface, but
602     // for logging purposes we would like to record the fact that this interface
603     // was requested.
604     result.Log(mTarget.get(), interfaceForQILog);
605     result = unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
606     ENSURE_HR_SUCCEEDED(result);
607     return result;
608   }
609 
610   // (2) Obtain a new target interface.
611 
612   // (2a) First, make sure that the target interface is available
613   // NB: We *MUST* query the correct interface! ICallEvents::Invoke casts its
614   // pvReceiver argument directly to the required interface! DO NOT assume
615   // that COM will use QI or upcast/downcast!
616   HRESULT hr;
617 
618   STAUniquePtr<IUnknown> targetInterface;
619   IUnknown* rawTargetInterface = nullptr;
620   hr =
621       QueryInterfaceTarget(interceptorIid, (void**)&rawTargetInterface, result);
622   targetInterface.reset(rawTargetInterface);
623   result = hr;
624   result.Log(mTarget.get(), targetInterface.get());
625   MOZ_ASSERT(SUCCEEDED(hr) || hr == E_NOINTERFACE);
626   if (hr == E_NOINTERFACE) {
627     return hr;
628   }
629   ENSURE_HR_SUCCEEDED(hr);
630 
631   // We *really* shouldn't be adding interceptors to proxies
632   MOZ_ASSERT(aIid != IID_IMarshal);
633 
634   // (3) Create a new COM interceptor to that interface that delegates its
635   // IUnknown to |this|.
636 
637   // Raise the refcount for stabilization purposes during aggregation
638   WeakReferenceSupport::StabilizeRefCount stabilizer(*this);
639 
640   hr = CreateInterceptor(interceptorIid,
641                          static_cast<WeakReferenceSupport*>(this),
642                          getter_AddRefs(unkInterceptor));
643   ENSURE_HR_SUCCEEDED(hr);
644 
645   // (4) Obtain the interceptor's ICallInterceptor interface and register our
646   // event sink.
647   RefPtr<ICallInterceptor> interceptor;
648   hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
649                                       (void**)getter_AddRefs(interceptor));
650   ENSURE_HR_SUCCEEDED(hr);
651 
652   hr = interceptor->RegisterSink(mEventSink);
653   ENSURE_HR_SUCCEEDED(hr);
654 
655   // (5) Now that we have this new COM interceptor, insert it into the map.
656   auto doInsertion = [&]() -> void {
657     // We might have raced with another thread, so first check that we don't
658     // already have an entry for this
659     MapEntry* entry = Lookup(interceptorIid);
660     if (entry && entry->mInterceptor) {
661       // Bug 1433046: Because of aggregation, the QI for |interceptor|
662       // AddRefed |this|, not |unkInterceptor|. Thus, releasing |unkInterceptor|
663       // will destroy the object. Before we do that, we must first release
664       // |interceptor|. Otherwise, |interceptor| would be invalidated when
665       // |unkInterceptor| is destroyed.
666       interceptor = nullptr;
667       unkInterceptor = entry->mInterceptor;
668     } else {
669       // MapEntry has a RefPtr to unkInterceptor, OTOH we must not touch the
670       // refcount for the target interface because we are just moving it into
671       // the map and its refcounting might not be thread-safe.
672       IUnknown* rawTargetInterface = targetInterface.release();
673       mInterceptorMap.AppendElement(
674           MapEntry(interceptorIid, unkInterceptor, rawTargetInterface));
675     }
676   };
677 
678   if (aAlreadyLocked) {
679     doInsertion();
680   } else {
681     MutexAutoLock lock(mInterceptorMapMutex);
682     doInsertion();
683   }
684 
685   hr = unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
686   ENSURE_HR_SUCCEEDED(hr);
687   return hr;
688 }
689 
690 HRESULT
QueryInterfaceTarget(REFIID aIid,void ** aOutput,TimeDuration * aOutDuration)691 Interceptor::QueryInterfaceTarget(REFIID aIid, void** aOutput,
692                                   TimeDuration* aOutDuration) {
693   // NB: This QI needs to run on the main thread because the target object
694   // is probably Gecko code that is not thread-safe. Note that this main
695   // thread invocation is *synchronous*.
696   if (!NS_IsMainThread() && tlsCreatingStdMarshal.get()) {
697     mStdMarshalMutex.AssertCurrentThreadOwns();
698     // COM queries for special interfaces such as IFastRundown when creating a
699     // marshaler. We don't want these being dispatched to the main thread,
700     // since this would cause a deadlock on mStdMarshalMutex if the main
701     // thread is also querying for IMarshal. If we do need to respond to these
702     // special interfaces, this should be done before this point; e.g. in
703     // Interceptor::QueryInterface like we do for INoMarshal.
704     return E_NOINTERFACE;
705   }
706 
707   MainThreadInvoker invoker;
708   HRESULT hr;
709   auto runOnMainThread = [&]() -> void {
710     MOZ_ASSERT(NS_IsMainThread());
711     hr = mTarget->QueryInterface(aIid, aOutput);
712   };
713   if (!invoker.Invoke(NS_NewRunnableFunction("Interceptor::QueryInterface",
714                                              runOnMainThread))) {
715     return E_FAIL;
716   }
717   if (aOutDuration) {
718     *aOutDuration = invoker.GetDuration();
719   }
720   return hr;
721 }
722 
723 HRESULT
QueryInterface(REFIID riid,void ** ppv)724 Interceptor::QueryInterface(REFIID riid, void** ppv) {
725   if (riid == IID_INoMarshal) {
726     // This entire library is designed around marshaling, so there's no point
727     // propagating this QI request all over the place!
728     return E_NOINTERFACE;
729   }
730 
731   return WeakReferenceSupport::QueryInterface(riid, ppv);
732 }
733 
734 HRESULT
WeakRefQueryInterface(REFIID aIid,IUnknown ** aOutInterface)735 Interceptor::WeakRefQueryInterface(REFIID aIid, IUnknown** aOutInterface) {
736   if (aIid == IID_IStdMarshalInfo) {
737     detail::ReentrySentinel sentinel(this);
738 
739     if (!sentinel.IsOutermost()) {
740       return E_NOINTERFACE;
741     }
742 
743     // Do not indicate that this interface is available unless we actually
744     // support it. We'll check that by looking for a successful call to
745     // IInterceptorSink::GetHandler()
746     CLSID dummy;
747     if (FAILED(mEventSink->GetHandler(WrapNotNull(&dummy)))) {
748       return E_NOINTERFACE;
749     }
750 
751     RefPtr<IStdMarshalInfo> std(this);
752     std.forget(aOutInterface);
753     return S_OK;
754   }
755 
756   if (aIid == IID_IMarshal) {
757     MutexAutoLock lock(mStdMarshalMutex);
758 
759     HRESULT hr;
760 
761     if (!mStdMarshalUnk) {
762       MOZ_ASSERT(!tlsCreatingStdMarshal.get());
763       tlsCreatingStdMarshal.set(true);
764       if (XRE_IsContentProcess()) {
765         hr = FastMarshaler::Create(static_cast<IWeakReferenceSource*>(this),
766                                    getter_AddRefs(mStdMarshalUnk));
767       } else {
768         hr = ::CoGetStdMarshalEx(static_cast<IWeakReferenceSource*>(this),
769                                  SMEXF_SERVER, getter_AddRefs(mStdMarshalUnk));
770       }
771       tlsCreatingStdMarshal.set(false);
772 
773       ENSURE_HR_SUCCEEDED(hr);
774     }
775 
776     if (!mStdMarshal) {
777       hr = mStdMarshalUnk->QueryInterface(IID_IMarshal, (void**)&mStdMarshal);
778       ENSURE_HR_SUCCEEDED(hr);
779 
780       // mStdMarshal is weak, so drop its refcount
781       mStdMarshal->Release();
782     }
783 
784     RefPtr<IMarshal> marshal(this);
785     marshal.forget(aOutInterface);
786     return S_OK;
787   }
788 
789   if (aIid == IID_IInterceptor) {
790     RefPtr<IInterceptor> intcpt(this);
791     intcpt.forget(aOutInterface);
792     return S_OK;
793   }
794 
795   if (aIid == IID_IDispatch) {
796     STAUniquePtr<IDispatch> disp;
797     IDispatch* rawDisp = nullptr;
798     HRESULT hr = QueryInterfaceTarget(aIid, (void**)&rawDisp);
799     ENSURE_HR_SUCCEEDED(hr);
800 
801     disp.reset(rawDisp);
802     return DispatchForwarder::Create(this, disp, aOutInterface);
803   }
804 
805   return GetInterceptorForIID(aIid, (void**)aOutInterface, nullptr);
806 }
807 
808 ULONG
AddRef()809 Interceptor::AddRef() { return WeakReferenceSupport::AddRef(); }
810 
811 ULONG
Release()812 Interceptor::Release() { return WeakReferenceSupport::Release(); }
813 
DisconnectRemotesForTarget(IUnknown * aTarget)814 /* static */ HRESULT Interceptor::DisconnectRemotesForTarget(
815     IUnknown* aTarget) {
816   MOZ_ASSERT(aTarget);
817 
818   detail::LiveSetAutoLock lock(GetLiveSet());
819 
820   // It is not an error if the interceptor doesn't exist, so we return
821   // S_FALSE instead of an error in that case.
822   RefPtr<IWeakReference> existingWeak(Move(GetLiveSet().Get(aTarget)));
823   if (!existingWeak) {
824     return S_FALSE;
825   }
826 
827   RefPtr<IWeakReferenceSource> existingStrong;
828   if (FAILED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
829     return S_FALSE;
830   }
831   // Since we now hold a strong ref on the interceptor, we may now release the
832   // lock.
833   lock.Unlock();
834 
835   return ::CoDisconnectObject(existingStrong, 0);
836 }
837 
838 }  // namespace mscom
839 }  // namespace mozilla
840