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

--HG--
extra : rebase_source : a650055c4adda3a1d99262e47f2b463074c6b935
This commit is contained in:
Aaron Klotz 2017-06-06 17:35:51 -06:00
parent 5361fd6640
commit 230ddb60a5
14 changed files with 417 additions and 20 deletions

View File

@ -49,6 +49,7 @@ RCINCLUDE = 'AccessibleHandler.rc'
# 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.

View File

@ -490,6 +490,7 @@ ContentChild* ContentChild::sSingleton;
ContentChild::ContentChild()
: mID(uint64_t(-1))
#if defined(XP_WIN) && defined(ACCESSIBILITY)
, mMainChromeTid(0)
, mMsaaID(0)
#endif
, mCanOverrideProcessName(true)
@ -2422,10 +2423,14 @@ ContentChild::RecvFlushMemory(const nsString& reason)
}
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

View File

@ -388,7 +388,8 @@ public:
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;
@ -499,6 +500,8 @@ public:
ContentParentId GetID() const { return mID; }
#if defined(XP_WIN) && defined(ACCESSIBILITY)
uint32_t GetChromeMainThreadId() const { return mMainChromeTid; }
uint32_t GetMsaaID() const { return mMsaaID; }
#endif
@ -699,6 +702,11 @@ private:
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.

View File

@ -1365,9 +1365,10 @@ ContentParent::Init()
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
@ -2773,9 +2774,10 @@ ContentParent::Observe(nsISupports* aSubject,
// 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

View File

@ -443,11 +443,13 @@ child:
/**
* 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).

View File

@ -6,11 +6,13 @@
#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"
@ -20,6 +22,7 @@
#include "nsDirectoryServiceUtils.h"
#include "nsRefPtrHashtable.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
namespace mozilla {
namespace mscom {
@ -142,6 +145,7 @@ Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
aDestContext == MSHCTX_DIFFERENTMACHINE) {
return E_INVALIDARG;
}
MOZ_ASSERT(mEventSink);
return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
}
@ -177,12 +181,60 @@ 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));
}

View File

@ -453,6 +453,7 @@ MainThreadHandoff::GetHandler(NotNull<CLSID*> aHandlerClsid)
if (!mHandlerProvider) {
return E_NOTIMPL;
}
return mHandlerProvider->GetHandler(aHandlerClsid);
}

249
ipc/mscom/Objref.cpp Normal file
View File

@ -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

32
ipc/mscom/Objref.h Normal file
View File

@ -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

View File

@ -4,10 +4,12 @@
* 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"
@ -139,6 +141,8 @@ IsVtableIndexFromParentInterface(REFIID aInterface, unsigned long aVtableIndex)
return result;
}
#if defined(MOZILLA_INTERNAL_API)
bool
IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
unsigned long aVtableIndexHint)
@ -223,6 +227,8 @@ IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
return false;
}
#endif // defined(MOZILLA_INTERNAL_API)
#endif // defined(ACCESSIBILITY)
} // namespace mscom

View File

@ -11,9 +11,7 @@
#include "nsString.h"
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)
#include <guiddef.h>
#endif // defined(ACCESSIBILITY)
struct IUnknown;
@ -31,8 +29,10 @@ void GUIDToString(REFGUID aGuid, nsAString& aOutString);
#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

View File

@ -12,6 +12,7 @@ EXPORTS.mozilla.mscom += [
'COMPtrHolder.h',
'EnsureMTA.h',
'MainThreadRuntime.h',
'Objref.h',
'ProxyStream.h',
'Ptr.h',
'Utils.h',
@ -21,6 +22,7 @@ UNIFIED_SOURCES += [
'AgileReference.cpp',
'EnsureMTA.cpp',
'MainThreadRuntime.cpp',
'Objref.cpp',
'ProxyStream.cpp',
'Utils.cpp',
]
@ -70,6 +72,8 @@ LOCAL_INCLUDES += [
'/xpcom/build',
]
DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -9,6 +9,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/mscom/Objref.h"
#include "nsWindowsHelpers.h"
#include <objbase.h>
@ -48,7 +49,7 @@ Handler::Handler(IUnknown* aOuter, HRESULT* aResult)
return;
}
// mInnerMarshal is a weak ref
// mUnmarshal is a weak ref
mUnmarshal->Release();
}
@ -169,14 +170,28 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
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(),
@ -185,6 +200,22 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
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;
}
@ -192,6 +223,7 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
// 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

View File

@ -8,8 +8,10 @@ Library('mscom_oop')
SOURCES += [
'../ActivationContext.cpp',
'../Objref.cpp',
'../Registration.cpp',
'../StructStream.cpp',
'../Utils.cpp',
]
UNIFIED_SOURCES += [
@ -26,6 +28,7 @@ OS_LIBS += [
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