gecko-dev/ipc/mscom/Interceptor.h
James Teh de10571c3c Bug 1654969: Always disable COM pings for mscom::FastMarshaler (and thus mscom::Interceptor). r=aklotz
Previously, we only did this when IsCallerExternalProcess() returned false.
There are three reasons for changing this:

1. There seem to be cases where IsCallerExternalProcess() returns true even when marshaling for a COM query in the MTA.
2. After bug 1627084, we pre-build a11y handler payloads on the main thread for bulk fetch calls. That will marshal interceptors. However, IsCallerExternalProcess() can't work in that case because it's not running on the thread on which the COM call is being handled.
3. If MSHLFLAGS_NOPING is used, Release calls from remote clients are never sent to the server. So, as soon as we use NOPING for our parent process, we're already going to leak references, even if we don't use NOPING for external callers. Put another way, as soon as we use NOPING for one caller, we may as well use it for all callers because COM pinging will never release the object anyway.

Differential Revision: https://phabricator.services.mozilla.com/D84778
2020-07-29 21:11:14 +00:00

198 lines
8.1 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_mscom_Interceptor_h
#define mozilla_mscom_Interceptor_h
#include <callobj.h>
#include <objidl.h>
#include <utility>
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/mscom/IHandlerProvider.h"
#include "mozilla/mscom/Ptr.h"
#include "mozilla/mscom/WeakRef.h"
#include "nsTArray.h"
namespace mozilla {
namespace mscom {
namespace detail {
class LiveSetAutoLock;
} // namespace detail
// {8831EB53-A937-42BC-9921-B3E1121FDF86}
DEFINE_GUID(IID_IInterceptorSink, 0x8831eb53, 0xa937, 0x42bc, 0x99, 0x21, 0xb3,
0xe1, 0x12, 0x1f, 0xdf, 0x86);
struct IInterceptorSink : public ICallFrameEvents, public HandlerProvider {
virtual STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) = 0;
};
// {3710799B-ECA2-4165-B9B0-3FA1E4A9B230}
DEFINE_GUID(IID_IInterceptor, 0x3710799b, 0xeca2, 0x4165, 0xb9, 0xb0, 0x3f,
0xa1, 0xe4, 0xa9, 0xb2, 0x30);
struct IInterceptor : public IUnknown {
virtual STDMETHODIMP GetTargetForIID(
REFIID aIid, InterceptorTargetPtr<IUnknown>& aTarget) = 0;
virtual STDMETHODIMP GetInterceptorForIID(REFIID aIid,
void** aOutInterceptor) = 0;
virtual STDMETHODIMP GetEventSink(IInterceptorSink** aSink) = 0;
};
/**
* The COM interceptor is the core functionality in mscom that allows us to
* redirect method calls to different threads. It emulates the vtable of a
* target interface. When a call is made on this emulated vtable, the call is
* packaged up into an instance of the ICallFrame interface which may be passed
* to other contexts for execution.
*
* In order to accomplish this, COM itself provides the CoGetInterceptor
* function, which instantiates an ICallInterceptor. Note, however, that
* ICallInterceptor only works on a single interface; we need to be able to
* interpose QueryInterface calls so that we can instantiate a new
* ICallInterceptor for each new interface that is requested.
*
* We accomplish this by using COM aggregation, which means that the
* ICallInterceptor delegates its IUnknown implementation to its outer object
* (the mscom::Interceptor we implement and control).
*
* ACHTUNG! mscom::Interceptor uses FastMarshaler to disable COM garbage
* collection. If you use this class, you *must* call
* Interceptor::DisconnectRemotesForTarget when an object is no longer relevant.
* Otherwise, the object will never be released, causing a leak.
*/
class Interceptor final : public WeakReferenceSupport,
public IStdMarshalInfo,
public IMarshal,
public IInterceptor {
public:
static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
REFIID aInitialIid, void** aOutInterface);
/**
* Disconnect all remote clients for a given target.
* Because Interceptors disable COM garbage collection to improve
* performance, they never receive Release calls from remote clients. If
* the object can be shut down while clients still hold a reference, this
* function can be used to force COM to disconnect all remote connections
* (using CoDisconnectObject) and thus release the associated references to
* the Interceptor, its target and any objects associated with the
* HandlerProvider.
* Note that the specified target must be the same IUnknown pointer used to
* create the Interceptor. Where there is multiple inheritance, querying for
* IID_IUnknown and calling this function with that pointer alone will not
* disconnect remotes for all interfaces. If you expect that the same object
* may be fetched with different initial interfaces, you should call this
* function once for each possible IUnknown pointer.
* @return S_OK if there was an Interceptor for the given target,
* S_FALSE if there was not.
*/
static HRESULT DisconnectRemotesForTarget(IUnknown* aTarget);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// IStdMarshalInfo
STDMETHODIMP GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
CLSID* aHandlerClsid) override;
// IMarshal
STDMETHODIMP GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) override;
STDMETHODIMP GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) override;
STDMETHODIMP MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) override;
STDMETHODIMP UnmarshalInterface(IStream* pStm, REFIID riid,
void** ppv) override;
STDMETHODIMP ReleaseMarshalData(IStream* pStm) override;
STDMETHODIMP DisconnectObject(DWORD dwReserved) override;
// IInterceptor
STDMETHODIMP GetTargetForIID(
REFIID aIid, InterceptorTargetPtr<IUnknown>& aTarget) override;
STDMETHODIMP GetInterceptorForIID(REFIID aIid,
void** aOutInterceptor) override;
STDMETHODIMP GetEventSink(IInterceptorSink** aSink) override {
RefPtr<IInterceptorSink> sink = mEventSink;
sink.forget(aSink);
return mEventSink ? S_OK : S_FALSE;
}
private:
struct MapEntry {
MapEntry(REFIID aIid, IUnknown* aInterceptor, IUnknown* aTargetInterface)
: mIID(aIid),
mInterceptor(aInterceptor),
mTargetInterface(aTargetInterface) {}
IID mIID;
RefPtr<IUnknown> mInterceptor;
IUnknown* mTargetInterface;
};
private:
explicit Interceptor(IInterceptorSink* aSink);
~Interceptor();
HRESULT GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
REFIID aTargetIid,
STAUniquePtr<IUnknown> aTarget,
void** aOutInterface);
HRESULT GetInterceptorForIID(REFIID aIid, void** aOutInterceptor,
MutexAutoLock* aAlreadyLocked);
MapEntry* Lookup(REFIID aIid);
HRESULT QueryInterfaceTarget(REFIID aIid, void** aOutput,
TimeDuration* aOutDuration = nullptr);
HRESULT WeakRefQueryInterface(REFIID aIid, IUnknown** aOutInterface) override;
HRESULT CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput);
REFIID MarshalAs(REFIID aIid) const;
HRESULT PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
RefPtr<IUnknown> aInterceptor, REFIID aTargetIid,
STAUniquePtr<IUnknown> aTarget);
private:
InterceptorTargetPtr<IUnknown> mTarget;
RefPtr<IInterceptorSink> mEventSink;
mozilla::Mutex mInterceptorMapMutex; // Guards mInterceptorMap
// Using a nsTArray since the # of interfaces is not going to be very high
nsTArray<MapEntry> mInterceptorMap;
mozilla::Mutex mStdMarshalMutex; // Guards mStdMarshalUnk and mStdMarshal
RefPtr<IUnknown> mStdMarshalUnk;
IMarshal* mStdMarshal; // WEAK
static MOZ_THREAD_LOCAL(bool) tlsCreatingStdMarshal;
};
template <typename InterfaceT>
inline HRESULT CreateInterceptor(STAUniquePtr<InterfaceT> aTargetInterface,
IInterceptorSink* aEventSink,
InterfaceT** aOutInterface) {
if (!aTargetInterface || !aEventSink) {
return E_INVALIDARG;
}
REFIID iidTarget = __uuidof(InterfaceT);
STAUniquePtr<IUnknown> targetUnknown(aTargetInterface.release());
return Interceptor::Create(std::move(targetUnknown), aEventSink, iidTarget,
(void**)aOutInterface);
}
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_Interceptor_h