Bug 1368571: Modify handler and interceptor marshaling code so that it strips out any handlers from proxies that are destined for non-Gecko processes; r=jimm
authorAaron Klotz <aklotz@mozilla.com>
Tue, 06 Jun 2017 17:35:51 -0600
changeset 411036 46699016ed9c06e82c88f4b255246430db2c9dd3
parent 411035 1c66da2b1e88e56a1b4de45a0721808eacb16338
child 411037 8c5509c3c923751a23257fedb9335e25fad94396
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1368571
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1368571: Modify handler and interceptor marshaling code so that it strips out any handlers from proxies that are destined for non-Gecko processes; r=jimm MozReview-Commit-ID: A1lCqvbQYAF There is no clean API-based solution to this, so instead I went grovelling through the DCOM wire protocol and was able to write a function that converts handler OBJREFs into standard OBJREFs. See also: https://msdn.microsoft.com/en-us/library/cc226801
accessible/ipc/win/handler/moz.build
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/PContent.ipdl
ipc/mscom/Interceptor.cpp
ipc/mscom/MainThreadHandoff.cpp
ipc/mscom/Objref.cpp
ipc/mscom/Objref.h
ipc/mscom/Utils.cpp
ipc/mscom/Utils.h
ipc/mscom/moz.build
ipc/mscom/oop/Handler.cpp
ipc/mscom/oop/moz.build
--- a/accessible/ipc/win/handler/moz.build
+++ b/accessible/ipc/win/handler/moz.build
@@ -44,16 +44,17 @@ OS_LIBS += [
 
 RCINCLUDE = 'AccessibleHandler.rc'
 
 # Since we are defining our own COM entry points (DllRegisterServer et al),
 # but we still want to be able to delegate some work to the generated code,
 # we add the prefix "Proxy" to all of the generated counterparts.
 DEFINES['ENTRY_PREFIX'] = 'Proxy'
 DEFINES['REGISTER_PROXY_DLL'] = True
+LIBRARY_DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
 
 # We want to generate distinct UUIDs on a per-channel basis, so we need
 # finer granularity than the standard preprocessor definitions offer.
 # These defines allow us to separate local builds from automated builds,
 # as well as separate beta from release.
 if CONFIG['MOZ_UPDATE_CHANNEL'] == 'default':
   DEFINES['USE_LOCAL_UUID'] = True
 elif CONFIG['MOZ_UPDATE_CHANNEL'] == 'beta':
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -485,16 +485,17 @@ private:
 
 NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback)
 
 ContentChild* ContentChild::sSingleton;
 
 ContentChild::ContentChild()
  : mID(uint64_t(-1))
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
+ , mMainChromeTid(0)
  , mMsaaID(0)
 #endif
  , mCanOverrideProcessName(true)
  , mIsAlive(true)
  , mShuttingDown(false)
 {
   // This process is a content process, so it's clearly running in
   // multiprocess mode!
@@ -2417,20 +2418,24 @@ ContentChild::RecvFlushMemory(const nsSt
     mozilla::services::GetObserverService();
   if (os) {
     os->NotifyObservers(nullptr, "memory-pressure", reason.get());
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentChild::RecvActivateA11y(const uint32_t& aMsaaID)
+ContentChild::RecvActivateA11y(const uint32_t& aMainChromeTid,
+                               const uint32_t& aMsaaID)
 {
 #ifdef ACCESSIBILITY
 #ifdef XP_WIN
+  MOZ_ASSERT(aMainChromeTid != 0);
+  mMainChromeTid = aMainChromeTid;
+
   MOZ_ASSERT(aMsaaID != 0);
   mMsaaID = aMsaaID;
 #endif // XP_WIN
 
   // Start accessibility in content process if it's running in chrome
   // process.
   GetOrCreateAccService(nsAccessibilityService::eMainProcess);
 #endif // ACCESSIBILITY
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -383,17 +383,18 @@ public:
 
   virtual mozilla::ipc::IPCResult RecvUpdateAppLocales(nsTArray<nsCString>&& aAppLocales) override;
   virtual mozilla::ipc::IPCResult RecvUpdateRequestedLocales(nsTArray<nsCString>&& aRequestedLocales) override;
 
   virtual mozilla::ipc::IPCResult RecvAddPermission(const IPC::Permission& permission) override;
 
   virtual mozilla::ipc::IPCResult RecvFlushMemory(const nsString& reason) override;
 
-  virtual mozilla::ipc::IPCResult RecvActivateA11y(const uint32_t& aMsaaID) override;
+  virtual mozilla::ipc::IPCResult RecvActivateA11y(const uint32_t& aMainChromeTid,
+                                                   const uint32_t& aMsaaID) override;
   virtual mozilla::ipc::IPCResult RecvShutdownA11y() override;
 
   virtual mozilla::ipc::IPCResult RecvGarbageCollect() override;
   virtual mozilla::ipc::IPCResult RecvCycleCollect() override;
 
   virtual mozilla::ipc::IPCResult RecvAppInfo(const nsCString& version, const nsCString& buildID,
                                               const nsCString& name, const nsCString& UAName,
                                               const nsCString& ID, const nsCString& vendor) override;
@@ -494,16 +495,18 @@ public:
 
   // Get the directory for IndexedDB files. We query the parent for this and
   // cache the value
   nsString &GetIndexedDBPath();
 
   ContentParentId GetID() const { return mID; }
 
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
+  uint32_t GetChromeMainThreadId() const { return mMainChromeTid; }
+
   uint32_t GetMsaaID() const { return mMsaaID; }
 #endif
 
   bool IsForBrowser() const { return mIsForBrowser; }
 
   virtual PFileDescriptorSetChild*
   SendPFileDescriptorSetConstructor(const FileDescriptor&) override;
 
@@ -695,16 +698,21 @@ private:
    *
    * We expect our content parent to set this ID immediately after opening a
    * channel to us.
    */
   ContentParentId mID;
 
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   /**
+   * The thread ID of the main thread in the chrome process.
+   */
+  uint32_t mMainChromeTid;
+
+  /**
    * This is an a11y-specific unique id for the content process that is
    * generated by the chrome process.
    */
   uint32_t mMsaaID;
 #endif
 
   AppInfo mAppInfo;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1360,19 +1360,20 @@ ContentParent::Init()
   }
 
 #ifdef ACCESSIBILITY
   // If accessibility is running in chrome process then start it in content
   // process.
   if (nsIPresShell::IsAccessibilityActive()) {
 #if defined(XP_WIN)
     Unused <<
-      SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
+      SendActivateA11y(::GetCurrentThreadId(),
+                       a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
 #else
-    Unused << SendActivateA11y(0);
+    Unused << SendActivateA11y(0, 0);
 #endif
   }
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
   Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
 #endif
 
@@ -2768,19 +2769,20 @@ ContentParent::Observe(nsISupports* aSub
   }
 #ifdef ACCESSIBILITY
   else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
     if (*aData == '1') {
       // Make sure accessibility is running in content process when
       // accessibility gets initiated in chrome process.
 #if defined(XP_WIN)
       Unused <<
-        SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
+        SendActivateA11y(::GetCurrentThreadId(),
+                         a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
 #else
-      Unused << SendActivateA11y(0);
+      Unused << SendActivateA11y(0, 0);
 #endif
     } else {
       // If possible, shut down accessibility in content process when
       // accessibility gets shutdown in chrome process.
       Unused << SendShutdownA11y();
     }
   }
 #endif
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -438,21 +438,23 @@ child:
 
     async FlushMemory(nsString reason);
 
     async GarbageCollect();
     async CycleCollect();
 
     /**
      * Start accessibility engine in content process.
+     * @param aTid is the thread ID of the chrome process main thread. Only used
+     *             on Windows; pass 0 on other platforms.
      * @param aMsaaID is an a11y-specific unique id for the content process
      *                that is generated by the chrome process. Only used on
      *                Windows; pass 0 on other platforms.
      */
-    async ActivateA11y(uint32_t aMsaaID);
+    async ActivateA11y(uint32_t aMainChromeTid, uint32_t aMsaaID);
 
     /**
      * Shutdown accessibility engine in content process (if not in use).
      */
     async ShutdownA11y();
 
     async AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName,
                   nsCString ID, nsCString vendor);
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -1,30 +1,33 @@
 /* -*- 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/. */
 
 #define INITGUID
 
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/Move.h"
 #include "mozilla/mscom/DispatchForwarder.h"
 #include "mozilla/mscom/Interceptor.h"
 #include "mozilla/mscom/InterceptorLog.h"
 #include "mozilla/mscom/MainThreadInvoker.h"
+#include "mozilla/mscom/Objref.h"
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/mscom/Utils.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsRefPtrHashtable.h"
 #include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
 
 namespace mozilla {
 namespace mscom {
 
 class LiveSet final
 {
 public:
   LiveSet()
@@ -137,16 +140,17 @@ Interceptor::~Interceptor()
 HRESULT
 Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
                                 CLSID* aHandlerClsid)
 {
   if (aDestContextPtr || !aHandlerClsid ||
       aDestContext == MSHCTX_DIFFERENTMACHINE) {
     return E_INVALIDARG;
   }
+
   MOZ_ASSERT(mEventSink);
   return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
 }
 
 HRESULT
 Interceptor::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
                                void* pvDestContext, DWORD mshlflags,
                                CLSID* pCid)
@@ -172,22 +176,70 @@ Interceptor::GetMarshalSizeMax(REFIID ri
   return hr;
 }
 
 HRESULT
 Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
                               DWORD dwDestContext, void* pvDestContext,
                               DWORD mshlflags)
 {
-  HRESULT hr = mStdMarshal->MarshalInterface(pStm, riid, pv, dwDestContext,
-                                             pvDestContext, mshlflags);
+  HRESULT hr;
+
+#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+  // Save the current stream position
+  LARGE_INTEGER seekTo;
+  seekTo.QuadPart = 0;
+
+  ULARGE_INTEGER objrefPos;
+
+  hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+
+  hr = mStdMarshal->MarshalInterface(pStm, riid, pv, dwDestContext,
+                                     pvDestContext, mshlflags);
   if (FAILED(hr)) {
     return hr;
   }
 
+#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+  if (XRE_IsContentProcess()) {
+    const DWORD chromeMainTid =
+      dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
+
+    /*
+     * CoGetCallerTID() gives us the caller's thread ID when that thread resides
+     * in a single-threaded apartment. Since our chrome main thread does live
+     * inside an STA, we will therefore be able to check whether the caller TID
+     * equals our chrome main thread TID. This enables us to distinguish
+     * between our chrome thread vs other out-of-process callers.
+     */
+    DWORD callerTid;
+    if (::CoGetCallerTID(&callerTid) == S_FALSE && callerTid != chromeMainTid) {
+      // The caller isn't our chrome process, so do not provide a handler.
+      // First, seek back to the stream position that we prevously saved.
+      seekTo.QuadPart = objrefPos.QuadPart;
+      hr = pStm->Seek(seekTo, STREAM_SEEK_SET, nullptr);
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      // Now strip out the handler.
+      if (!StripHandlerFromOBJREF(WrapNotNull(pStm))) {
+        return E_FAIL;
+      }
+
+      return S_OK;
+    }
+  }
+#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+
   return mEventSink->WriteHandlerPayload(WrapNotNull(pStm));
 }
 
 HRESULT
 Interceptor::UnmarshalInterface(IStream* pStm, REFIID riid,
                                 void** ppv)
 {
   return mStdMarshal->UnmarshalInterface(pStm, riid, ppv);
--- a/ipc/mscom/MainThreadHandoff.cpp
+++ b/ipc/mscom/MainThreadHandoff.cpp
@@ -448,16 +448,17 @@ MainThreadHandoff::SetInterceptor(IWeakR
 }
 
 HRESULT
 MainThreadHandoff::GetHandler(NotNull<CLSID*> aHandlerClsid)
 {
   if (!mHandlerProvider) {
     return E_NOTIMPL;
   }
+
   return mHandlerProvider->GetHandler(aHandlerClsid);
 }
 
 HRESULT
 MainThreadHandoff::GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize)
 {
   if (!mHandlerProvider) {
     return E_NOTIMPL;
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/Objref.cpp
@@ -0,0 +1,249 @@
+/* -*- 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/. */
+
+#include "mozilla/mscom/Objref.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/mscom/Utils.h"
+#include "mozilla/UniquePtr.h"
+
+#include <guiddef.h>
+#include <objidl.h>
+
+namespace {
+
+#pragma pack(push, 1)
+
+typedef uint64_t OID;
+typedef uint64_t OXID;
+typedef GUID IPID;
+
+struct STDOBJREF
+{
+  uint32_t      mFlags;
+  uint32_t      mPublicRefs;
+  OXID          mOxid;
+  OID           mOid;
+  IPID          mIpid;
+};
+
+enum STDOBJREF_FLAGS
+{
+  SORF_PING = 0,
+  SORF_NOPING = 0x1000
+};
+
+struct DUALSTRINGARRAY
+{
+  static size_t SizeFromNumEntries(const uint16_t aNumEntries)
+  {
+    return sizeof(mNumEntries) + sizeof(mSecurityOffset) +
+           aNumEntries * sizeof(uint16_t);
+  }
+
+  size_t SizeOf() const
+  {
+    return SizeFromNumEntries(mNumEntries);
+  }
+
+  uint16_t  mNumEntries;
+  uint16_t  mSecurityOffset;
+  uint16_t  mStringArray[1]; // Length is mNumEntries
+};
+
+struct OBJREF_STANDARD
+{
+  size_t SizeOf() const
+  {
+    return sizeof(mStd) + mResAddr.SizeOf();
+  }
+
+  STDOBJREF       mStd;
+  DUALSTRINGARRAY mResAddr;
+};
+
+struct OBJREF_HANDLER
+{
+  size_t SizeOf() const
+  {
+    return sizeof(mStd) + sizeof(mClsid) + mResAddr.SizeOf();
+  }
+
+  STDOBJREF       mStd;
+  CLSID           mClsid;
+  DUALSTRINGARRAY mResAddr;
+};
+
+enum OBJREF_FLAGS
+{
+  OBJREF_TYPE_STANDARD = 0x00000001UL,
+  OBJREF_TYPE_HANDLER = 0x00000002UL,
+  OBJREF_TYPE_CUSTOM = 0x00000004UL,
+  OBJREF_TYPE_EXTENDED = 0x00000008UL,
+};
+
+struct OBJREF
+{
+  size_t SizeOf() const
+  {
+    size_t size = sizeof(mSignature) + sizeof(mFlags) + sizeof(mIid);
+
+    switch (mFlags) {
+      case OBJREF_TYPE_STANDARD:
+        size += mObjRefStd.SizeOf();
+        break;
+      case OBJREF_TYPE_HANDLER:
+        size += mObjRefHandler.SizeOf();
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unsupported OBJREF type");
+        return 0;
+    }
+
+    return size;
+  }
+
+  uint32_t  mSignature;
+  uint32_t  mFlags;
+  IID       mIid;
+  union {
+    OBJREF_STANDARD mObjRefStd;
+    OBJREF_HANDLER  mObjRefHandler;
+    // There are others but we're not supporting them here
+  };
+};
+
+enum OBJREF_SIGNATURES
+{
+  OBJREF_SIGNATURE = 0x574F454DUL
+};
+
+#pragma pack(pop)
+
+struct ByteArrayDeleter
+{
+  void operator()(void* aPtr)
+  {
+    delete[] reinterpret_cast<uint8_t*>(aPtr);
+  }
+};
+
+template <typename T>
+using VarStructUniquePtr = mozilla::UniquePtr<T, ByteArrayDeleter>;
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace mscom {
+
+bool
+StripHandlerFromOBJREF(NotNull<IStream*> aStream)
+{
+  // Get current stream position
+  LARGE_INTEGER seekTo;
+  seekTo.QuadPart = 0;
+
+  ULARGE_INTEGER objrefPos;
+
+  HRESULT hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  ULONG bytesRead;
+
+  uint32_t signature;
+  hr = aStream->Read(&signature, sizeof(signature), &bytesRead);
+  if (FAILED(hr) || bytesRead != sizeof(signature) ||
+      signature != OBJREF_SIGNATURE) {
+    return false;
+  }
+
+  uint32_t type;
+  hr = aStream->Read(&type, sizeof(type), &bytesRead);
+  if (FAILED(hr) || bytesRead != sizeof(type) ||
+      type != OBJREF_TYPE_HANDLER) {
+    return false;
+  }
+
+  IID iid;
+  hr = aStream->Read(&iid, sizeof(iid), &bytesRead);
+  if (FAILED(hr) || bytesRead != sizeof(iid) || !IsValidGUID(iid)) {
+    return false;
+  }
+
+  // Seek past fixed-size STDOBJREF and CLSID
+  seekTo.QuadPart = sizeof(STDOBJREF) + sizeof(CLSID);
+  hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  uint16_t numEntries;
+  hr = aStream->Read(&numEntries, sizeof(numEntries), &bytesRead);
+  if (FAILED(hr) || bytesRead != sizeof(numEntries)) {
+    return false;
+  }
+
+  // We'll try to use a stack buffer if resAddrSize <= kMinDualStringArraySize
+  const uint32_t kMinDualStringArraySize = 12;
+  uint16_t staticResAddrBuf[kMinDualStringArraySize / sizeof(uint16_t)];
+
+  size_t resAddrSize = DUALSTRINGARRAY::SizeFromNumEntries(numEntries);
+
+  DUALSTRINGARRAY* resAddr;
+  VarStructUniquePtr<DUALSTRINGARRAY> dynamicResAddrBuf;
+
+  if (resAddrSize <= kMinDualStringArraySize) {
+    resAddr = reinterpret_cast<DUALSTRINGARRAY*>(staticResAddrBuf);
+  } else {
+    dynamicResAddrBuf.reset(
+        reinterpret_cast<DUALSTRINGARRAY*>(new uint8_t[resAddrSize]));
+    resAddr = dynamicResAddrBuf.get();
+  }
+
+  resAddr->mNumEntries = numEntries;
+
+  // Because we've already read numEntries
+  ULONG bytesToRead = resAddrSize - sizeof(numEntries);
+
+  hr = aStream->Read(&resAddr->mSecurityOffset, bytesToRead, &bytesRead);
+  if (FAILED(hr) || bytesRead != bytesToRead) {
+    return false;
+  }
+
+  // Signature doesn't change so we'll seek past that
+  seekTo.QuadPart = objrefPos.QuadPart + sizeof(signature);
+  hr = aStream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  ULONG bytesWritten;
+
+  uint32_t newType = OBJREF_TYPE_STANDARD;
+  hr = aStream->Write(&newType, sizeof(newType), &bytesWritten);
+  if (FAILED(hr) || bytesWritten != sizeof(newType)) {
+    return false;
+  }
+
+  // Skip past IID and STDOBJREF since those don't change
+  seekTo.QuadPart = sizeof(IID) + sizeof(STDOBJREF);
+  hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  hr = aStream->Write(resAddr, resAddrSize, &bytesWritten);
+  if (FAILED(hr) || bytesWritten != resAddrSize) {
+    return false;
+  }
+
+  return true;
+}
+
+} // namespace mscom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/Objref.h
@@ -0,0 +1,32 @@
+/* -*- 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_Objref_h
+#define mozilla_mscom_Objref_h
+
+#include "mozilla/NotNull.h"
+
+struct IStream;
+
+namespace mozilla {
+namespace mscom {
+
+/**
+ * Given a buffer containing a serialized proxy to an interface with a handler,
+ * this function strips out the handler and converts it to a standard one.
+ * @param aStream IStream whose pointer is positioned at the beginning of the
+ *                OBJREF to be stripped. There should be nothing else written
+ *                to the stream past the current OBJREF.
+ * @return true if the handler was successfully stripped, otherwise false.
+ */
+bool
+StripHandlerFromOBJREF(NotNull<IStream*> aStream);
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_Objref_h
+
--- a/ipc/mscom/Utils.cpp
+++ b/ipc/mscom/Utils.cpp
@@ -1,18 +1,20 @@
 /* -*- 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/. */
 
-#ifdef ACCESSIBILITY
+#if defined(ACCESSIBILITY)
 #include "mozilla/mscom/Registration.h"
+#if defined(MOZILLA_INTERNAL_API)
 #include "nsTArray.h"
 #endif
+#endif
 
 #include "mozilla/mscom/Utils.h"
 #include "mozilla/RefPtr.h"
 
 #include <objbase.h>
 #include <objidl.h>
 
 namespace mozilla {
@@ -134,16 +136,18 @@ IsVtableIndexFromParentInterface(REFIID 
   }
 
   bool result = IsVtableIndexFromParentInterface(typeAttr, aVtableIndex);
 
   typeInfo->ReleaseTypeAttr(typeAttr);
   return result;
 }
 
+#if defined(MOZILLA_INTERNAL_API)
+
 bool
 IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
                                   unsigned long aVtableIndexHint)
 {
   if (aInterface == aFrom) {
     return true;
   }
 
@@ -218,12 +222,14 @@ IsInterfaceEqualToOrInheritedFrom(REFIID
 
       typeInfos.AppendElement(Move(nextTypeInfo));
     }
   }
 
   return false;
 }
 
+#endif // defined(MOZILLA_INTERNAL_API)
+
 #endif // defined(ACCESSIBILITY)
 
 } // namespace mscom
 } // namespace mozilla
--- a/ipc/mscom/Utils.h
+++ b/ipc/mscom/Utils.h
@@ -6,19 +6,17 @@
 
 #ifndef mozilla_mscom_Utils_h
 #define mozilla_mscom_Utils_h
 
 #if defined(MOZILLA_INTERNAL_API)
 #include "nsString.h"
 #endif // defined(MOZILLA_INTERNAL_API)
 
-#if defined(ACCESSIBILITY)
 #include <guiddef.h>
-#endif // defined(ACCESSIBILITY)
 
 struct IUnknown;
 
 namespace mozilla {
 namespace mscom {
 
 bool IsCurrentThreadMTA();
 bool IsProxy(IUnknown* aUnknown);
@@ -26,17 +24,19 @@ bool IsValidGUID(REFGUID aCheckGuid);
 
 #if defined(MOZILLA_INTERNAL_API)
 void GUIDToString(REFGUID aGuid, nsAString& aOutString);
 #endif // defined(MOZILLA_INTERNAL_API)
 
 #if defined(ACCESSIBILITY)
 bool IsVtableIndexFromParentInterface(REFIID aInterface,
                                       unsigned long aVtableIndex);
+#if defined(MOZILLA_INTERNAL_API)
 bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
                                        unsigned long aVtableIndexHint);
+#endif // defined(MOZILLA_INTERNAL_API)
 #endif // defined(ACCESSIBILITY)
 
 } // namespace mscom
 } // namespace mozilla
 
 #endif // mozilla_mscom_Utils_h
 
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -7,25 +7,27 @@
 EXPORTS.mozilla.mscom += [
     'Aggregation.h',
     'AgileReference.h',
     'AsyncInvoker.h',
     'COMApartmentRegion.h',
     'COMPtrHolder.h',
     'EnsureMTA.h',
     'MainThreadRuntime.h',
+    'Objref.h',
     'ProxyStream.h',
     'Ptr.h',
     'Utils.h',
 ]
 
 UNIFIED_SOURCES += [
     'AgileReference.cpp',
     'EnsureMTA.cpp',
     'MainThreadRuntime.cpp',
+    'Objref.cpp',
     'ProxyStream.cpp',
     'Utils.cpp',
 ]
 
 if CONFIG['MOZ_CRASHREPORTER']:
     UNIFIED_SOURCES += [
         'InterfaceRegistrationAnnotator.cpp',
     ]
@@ -65,14 +67,16 @@ if CONFIG['ACCESSIBILITY']:
         'StructStream.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '/xpcom/base',
     '/xpcom/build',
 ]
 
+DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "Disability Access APIs")
--- a/ipc/mscom/oop/Handler.cpp
+++ b/ipc/mscom/oop/Handler.cpp
@@ -4,16 +4,17 @@
  * 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/. */
 
 #include "Handler.h"
 #include "Module.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/mscom/Objref.h"
 #include "nsWindowsHelpers.h"
 
 #include <objbase.h>
 #include <shlwapi.h>
 #include <string.h>
 
 /* WARNING! The code in this file may be loaded into the address spaces of other
    processes! It MUST NOT link against xul.dll or other Gecko binaries! Only
@@ -43,17 +44,17 @@ Handler::Handler(IUnknown* aOuter, HRESU
     return;
   }
 
   *aResult = mInnerUnk->QueryInterface(IID_IMarshal, (void**)&mUnmarshal);
   if (FAILED(*aResult)) {
     return;
   }
 
-  // mInnerMarshal is a weak ref
+  // mUnmarshal is a weak ref
   mUnmarshal->Release();
 }
 
 HRESULT
 Handler::InternalQueryInterface(REFIID riid, void** ppv)
 {
   if (!ppv) {
     return E_INVALIDARG;
@@ -164,39 +165,70 @@ Handler::MarshalInterface(IStream* pStm,
                           DWORD mshlflags)
 {
   // We do not necessarily want to use the pv that COM is giving us; we may want
   // to marshal a different proxy that is more appropriate to what we're
   // wrapping...
   RefPtr<IUnknown> unkToMarshal;
   HRESULT hr;
 
+#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+  LARGE_INTEGER seekTo;
+  seekTo.QuadPart = 0;
+
+  ULARGE_INTEGER objrefPos;
+
+  // Save the current position as it points to the location where the OBJREF
+  // will be written.
+  hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // When marshaling without a handler, we just use the riid as passed in.
+  REFIID marshalAs = riid;
+#else
   REFIID marshalAs = MarshalAs(riid);
-  if (marshalAs == riid) {
-    unkToMarshal = static_cast<IUnknown*>(pv);
-  } else {
-    hr = mInnerUnk->QueryInterface(marshalAs, getter_AddRefs(unkToMarshal));
-    if (FAILED(hr)) {
-      return hr;
-    }
+#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+
+  hr = mInnerUnk->QueryInterface(marshalAs, getter_AddRefs(unkToMarshal));
+  if (FAILED(hr)) {
+    return hr;
   }
 
   hr = mUnmarshal->MarshalInterface(pStm, marshalAs, unkToMarshal.get(),
                                     dwDestContext, pvDestContext, mshlflags);
   if (FAILED(hr)) {
     return hr;
   }
 
+#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+  // Now the OBJREF has been written, so seek back to its beginning (the
+  // position that we saved earlier).
+  seekTo.QuadPart = objrefPos.QuadPart;
+  hr = pStm->Seek(seekTo, STREAM_SEEK_SET, nullptr);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Now strip out the handler.
+  if (!StripHandlerFromOBJREF(WrapNotNull(pStm))) {
+    return E_FAIL;
+  }
+
+  return S_OK;
+#else
   if (!HasPayload()) {
     return S_OK;
   }
 
   // Unfortunately when COM re-marshals a proxy that prevouisly had a payload,
   // we must re-serialize it.
   return WriteHandlerPayload(pStm, marshalAs);
+#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
 }
 
 HRESULT
 Handler::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv)
 {
   REFIID unmarshalAs = MarshalAs(riid);
   HRESULT hr = mUnmarshal->UnmarshalInterface(pStm, unmarshalAs, ppv);
   if (FAILED(hr)) {
--- a/ipc/mscom/oop/moz.build
+++ b/ipc/mscom/oop/moz.build
@@ -3,34 +3,37 @@
 # 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/.
 
 Library('mscom_oop')
 
 SOURCES += [
     '../ActivationContext.cpp',
+    '../Objref.cpp',
     '../Registration.cpp',
     '../StructStream.cpp',
+    '../Utils.cpp',
 ]
 
 UNIFIED_SOURCES += [
     'Handler.cpp',
     'Module.cpp',
 ]
 
 OS_LIBS += [
     'ole32',
     'oleaut32',
     'shlwapi',
 ]
 
 LIBRARY_DEFINES['UNICODE'] = True
 LIBRARY_DEFINES['_UNICODE'] = True
 LIBRARY_DEFINES['MOZ_NO_MOZALLOC'] = True
+LIBRARY_DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
 
 DISABLE_STL_WRAPPING = True
 NO_EXPAND_LIBS = True
 FORCE_STATIC_LIB = True
 
 # This DLL may be loaded into other processes, so we need static libs for
 # Windows 7 and Windows 8.
 USE_STATIC_LIBS = True