зеркало из https://github.com/mozilla/gecko-dev.git
1439 строки
51 KiB
C++
1439 строки
51 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 dom_plugins_ipc_PluginHooksWin_h
|
|
#define dom_plugins_ipc_PluginHooksWin_h 1
|
|
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include "base/task.h"
|
|
#include "mozilla/ipc/ProcessChild.h"
|
|
#include "FunctionBrokerChild.h"
|
|
#include "mtransport/runnable_utils.h"
|
|
#include "PluginMessageUtils.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "FunctionHook.h"
|
|
#include "FunctionBrokerIPCUtils.h"
|
|
|
|
#if defined(XP_WIN)
|
|
# define SECURITY_WIN32
|
|
# include <security.h>
|
|
# include <wininet.h>
|
|
# include <schnlsp.h>
|
|
# if defined(MOZ_SANDBOX)
|
|
# include "sandboxPermissions.h"
|
|
# endif
|
|
#endif // defined(XP_WIN)
|
|
|
|
/**
|
|
* This functionality supports automatic method hooking (FunctionHook) and
|
|
* brokering (FunctionBroker), which are used to intercept system calls
|
|
* (using the nsDllInterceptor) and replace them with new functionality (hook)
|
|
* or proxy them on another process (broker).
|
|
* There isn't much of a public interface to this (see FunctionHook
|
|
* for initialization functionality) since the majority of the behavior
|
|
* comes from intercepting calls to DLL methods (making those DLL methods the
|
|
* public interface). Generic RPC can be achieved without DLLs or function
|
|
* interception by directly calling the FunctionBroker::InterceptorStub.
|
|
*
|
|
* The system supports the most common logic surrounding brokering by allowing
|
|
* the client to supply strategies for them. Some examples of common tasks that
|
|
* are supported by automatic brokering:
|
|
*
|
|
* * Intercepting a new Win32 method:
|
|
*
|
|
* Step 1: Add a typedef or subclass of either FunctionHook (non-brokering) or
|
|
* FunctionBroker (automatic brokering) to FunctionBroker.cpp, using a new
|
|
* FunctionHookID (added to that enum).
|
|
* For example:
|
|
* typedef FunctionBroker<ID_GetKeyState, decltype(GetKeyState)> GetKeyStateFB
|
|
* Use a subclass instead of a typedef if you need to maintain data or state.
|
|
*
|
|
* Step 2: Add an instance of that object to the FunctionHookList in
|
|
* AddFunctionHook(FunctionHookList&) or
|
|
* AddBrokeredFunctionHook(FunctionHookList&).
|
|
* This typically just means calling the constructor with the correct info.
|
|
* At a minimum, this means supplying the names of the DLL and method to
|
|
* broker, and a pointer to the original version of the method.
|
|
* For example:
|
|
* aHooks[ID_GetKeyState] =
|
|
* new GetKeyStateFB("user32.dll", "GetKeyState", &GetKeyState);
|
|
*
|
|
* Step 3: If brokering, make sure the system can (un)marshal the parameters,
|
|
* either by the means below or by adding the type to IpdlTuple, which we use
|
|
* for type-safely (un)marshaling the parameter list.
|
|
*
|
|
* * Only brokering _some_ calls to the method:
|
|
*
|
|
* FunctionBroker's constructor allows the user to supply a ShouldBroker
|
|
* function, which takes the parameters of the method call and returns false
|
|
* if we should use the original method instead of brokering.
|
|
*
|
|
* * Only passing _some_ parameters to the brokering process / returning
|
|
* parameters to client:
|
|
*
|
|
* If a system call changes a parameter call-by-reference style then the
|
|
* parameter's value needs to be returned to the client. The FunctionBroker
|
|
* has "phase" (request/response) objects that it uses to determine which
|
|
* parameters are sent/returned. This example tells InternetWriteFileFB to
|
|
* return its third parameter:
|
|
* template<> template<>
|
|
* struct InternetWriteFileFB::Response::Info::ShouldMarshal<3> {
|
|
* static const bool value = true;
|
|
* };
|
|
* By default, all parameters have ShouldMarshal set in the request phase
|
|
* and only the return value (parameter -1) has it set in the response phase.
|
|
*
|
|
* * Marshalling special parameter/return types:
|
|
*
|
|
* The IPCTypeMap in FunctionBroker maps a parameter or return type
|
|
* to a type that IpdlTuple knows how to marshal. By default, the map is
|
|
* the identity but some types need special handling.
|
|
* The map is endpoint-specific (it is a member of the EndpointHandler),
|
|
* so a different type can be used
|
|
* for client -> server and for server -> client. Note that the
|
|
* types must be able to Copy() from one another -- the default Copy()
|
|
* implementation uses the type's assignment operator.
|
|
* The EndpointHandler itself is a template parameter of the FunctionBroker.
|
|
* The default EndpointHandler recognizes basic types.
|
|
* See e.g. FileDlgEndpointHandler<CLIENT>::IPCTypeMap<LPOPENFILENAMEW>
|
|
* for an example of specialization.
|
|
*
|
|
* * Anything more complex involving parameter transmission:
|
|
*
|
|
* Sometimes marshaling parameters can require something more complex. In
|
|
* those cases, you will need to specialize the Marshal and Unmarshal
|
|
* methods of the request or response handler and perform your complex logic
|
|
* there. A wise approach is to map your complex parameters into a simpler
|
|
* parameter list and delegate the Marshal/Unmarshal calls to them. For
|
|
* example, an API might take a void* and an int as a buffer and length.
|
|
* Obviously a void* cannot generally be marshaled. However, we can delegate
|
|
* this call to a parameter list that takes a string in place of the buffer and
|
|
* length. Something like:
|
|
*
|
|
* typedef RequestHandler<ID_HookedFunc,
|
|
* int HOOK_CALL (nsDependentCSubstring)>
|
|
* HookedFuncDelegateReq;
|
|
*
|
|
* template<>
|
|
* void HookedFuncFB::Request::Marshal(IpdlTuple& aTuple, const void*& aBuf,
|
|
* const int& aBufLen)
|
|
* {
|
|
* MOZ_ASSERT(nWritten);
|
|
* HookedFuncDelegateReq::Marshal(aTuple,
|
|
* nsDependentCSubstring(aBuf, aBufLen));
|
|
* }
|
|
*
|
|
* template<>
|
|
* bool HookedFuncFB::Request::Unmarshal(ServerCallData& aScd, const IpdlTuple&
|
|
* aTuple, void*& aBuf, int& aBufLen)
|
|
* {
|
|
* nsDependentCSubstring str;
|
|
* if (!HookedFuncDelegateReq::Unmarshal(aScd, aTuple, str)) {
|
|
* return false;
|
|
* }
|
|
*
|
|
* // Request phase unmarshal uses ServerCallData for dynamically-allocating
|
|
* // memory.
|
|
* aScd.AllocateString(str, aBuf, false);
|
|
* aBufLen = str.Length();
|
|
* return true;
|
|
* }
|
|
*
|
|
* See e.g. InternetWriteFileFB for a complete example of delegation.
|
|
*
|
|
* * Brokering but need the server to do more than just run the function:
|
|
*
|
|
* Specialize the FunctionBroker's RunFunction. By default, it just runs
|
|
* the function. See GetSaveFileNameWFB for an example that does more.
|
|
*
|
|
*/
|
|
|
|
namespace mozilla {
|
|
namespace plugins {
|
|
|
|
#if defined(XP_WIN)
|
|
|
|
// Currently, all methods we hook use the WINAPI calling convention.
|
|
# define HOOK_CALL WINAPI
|
|
|
|
typedef std::pair<ULONG_PTR, ULONG_PTR> UlongPair;
|
|
typedef std::map<UlongPair, uint64_t> UlongPairToIdMap;
|
|
extern UlongPairToIdMap sPairToIdMap;
|
|
typedef std::map<uint64_t, UlongPair> IdToUlongPairMap;
|
|
extern IdToUlongPairMap sIdToPairMap;
|
|
typedef std::map<void*, uint64_t> PtrToIdMap;
|
|
extern PtrToIdMap sPtrToIdMap;
|
|
typedef std::map<uint64_t, void*> IdToPtrMap;
|
|
extern IdToPtrMap sIdToPtrMap;
|
|
|
|
#else // defined(XP_WIN)
|
|
|
|
// Any methods we hook use the default calling convention.
|
|
# define HOOK_CALL
|
|
|
|
#endif // defined(XP_WIN)
|
|
|
|
inline bool IsOdd(uint64_t aVal) { return aVal & 1; }
|
|
|
|
// This enum is used to track if this process is currently running the client
|
|
// or server side of brokering.
|
|
enum Endpoint { SERVER, CLIENT };
|
|
inline const char* EndpointMsg(Endpoint aVal) {
|
|
return aVal == SERVER ? "SERVER" : "CLIENT";
|
|
}
|
|
|
|
template <typename ParamType>
|
|
inline void LogParameterValue(int aIndex, const ParamType& aParam) {
|
|
// To avoid overhead, don't do this in release.
|
|
#ifdef DEBUG
|
|
if (!MOZ_LOG_TEST(sPluginHooksLog, LogLevel::Verbose)) {
|
|
return;
|
|
}
|
|
std::wstring paramString;
|
|
IPC::LogParam(aParam, ¶mString);
|
|
HOOK_LOG(LogLevel::Verbose,
|
|
("Parameter %d: %S", aIndex, paramString.c_str()));
|
|
#endif
|
|
}
|
|
|
|
// This specialization is needed to log the common pattern where null is used
|
|
// as a fixed value for a pointer-type that is unknown to IPC.
|
|
template <typename ParamType>
|
|
inline void LogParameterValue(int aIndex, ParamType* const& aParam) {
|
|
#ifdef DEBUG
|
|
HOOK_LOG(LogLevel::Verbose,
|
|
("Parameter %d: pointer value - %p", aIndex, aParam));
|
|
#endif
|
|
}
|
|
|
|
template <>
|
|
inline void LogParameterValue(int aIndex, const nsDependentCSubstring& aParam) {
|
|
#ifdef DEBUG
|
|
HOOK_LOG(LogLevel::Verbose,
|
|
("Parameter %d : %s", aIndex, FormatBlob(aParam).Data()));
|
|
#endif
|
|
}
|
|
|
|
template <>
|
|
inline void LogParameterValue(int aIndex, char* const& aParam) {
|
|
#ifdef DEBUG
|
|
// A char* can be a block of raw memory.
|
|
nsDependentCSubstring str;
|
|
if (aParam) {
|
|
str.Rebind(const_cast<char*>(aParam),
|
|
strnlen(aParam, MAX_BLOB_CHARS_TO_LOG));
|
|
} else {
|
|
str.SetIsVoid(true);
|
|
}
|
|
LogParameterValue(aIndex, str);
|
|
#endif
|
|
}
|
|
|
|
template <>
|
|
inline void LogParameterValue(int aIndex, const char* const& aParam) {
|
|
#ifdef DEBUG
|
|
LogParameterValue(aIndex, const_cast<char* const&>(aParam));
|
|
#endif
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
template <>
|
|
inline void LogParameterValue(int aIndex, const SEC_GET_KEY_FN& aParam) {
|
|
# ifdef DEBUG
|
|
MOZ_ASSERT(aParam == nullptr);
|
|
HOOK_LOG(LogLevel::Verbose, ("Parameter %d: null function.", aIndex));
|
|
# endif
|
|
}
|
|
|
|
template <>
|
|
inline void LogParameterValue(int aIndex, LPVOID* const& aParam) {
|
|
# ifdef DEBUG
|
|
MOZ_ASSERT(aParam == nullptr);
|
|
HOOK_LOG(LogLevel::Verbose, ("Parameter %d: null void pointer.", aIndex));
|
|
# endif
|
|
}
|
|
#endif // defined(XP_WIN)
|
|
|
|
// Used to check if a fixed parameter value is equal to the parameter given
|
|
// in the original function call.
|
|
template <typename ParamType>
|
|
inline bool ParameterEquality(const ParamType& aParam1,
|
|
const ParamType& aParam2) {
|
|
return aParam1 == aParam2;
|
|
}
|
|
|
|
// Specialization: char* equality is string equality
|
|
template <>
|
|
inline bool ParameterEquality(char* const& aParam1, char* const& aParam2) {
|
|
return ((!aParam1 && !aParam2) ||
|
|
(aParam1 && aParam2 && !strcmp(aParam1, aParam2)));
|
|
}
|
|
|
|
// Specialization: const char* const equality is string equality
|
|
template <>
|
|
inline bool ParameterEquality(const char* const& aParam1,
|
|
const char* const& aParam2) {
|
|
return ParameterEquality(const_cast<char* const&>(aParam1),
|
|
const_cast<char* const&>(aParam2));
|
|
}
|
|
|
|
/**
|
|
* A type map _from_ the type of a parameter in the original function
|
|
* we are brokering _to_ a type that we can marshal. We must be able
|
|
* to Copy() the marshaled type using the parameter type.
|
|
* The default maps from type T back to type T.
|
|
*/
|
|
template <typename OrigType>
|
|
struct IPCTypeMap {
|
|
typedef OrigType ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<char*> {
|
|
typedef nsDependentCSubstring ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<const char*> {
|
|
typedef nsDependentCSubstring ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<wchar_t*> {
|
|
typedef nsString ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<const wchar_t*> {
|
|
typedef nsString ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<long> {
|
|
typedef int32_t ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<unsigned long> {
|
|
typedef uint32_t ipc_type;
|
|
};
|
|
|
|
#if defined(XP_WIN)
|
|
template <>
|
|
struct IPCTypeMap<PSecHandle> {
|
|
typedef uint64_t ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<PTimeStamp> {
|
|
typedef uint64_t ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<void*> {
|
|
typedef uint64_t ipc_type;
|
|
}; // HANDLEs
|
|
template <>
|
|
struct IPCTypeMap<HWND> {
|
|
typedef NativeWindowHandle ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<PSCHANNEL_CRED> {
|
|
typedef IPCSchannelCred ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<LPINTERNET_BUFFERSA> {
|
|
typedef IPCInternetBuffers ipc_type;
|
|
};
|
|
template <>
|
|
struct IPCTypeMap<LPDWORD> {
|
|
typedef uint32_t ipc_type;
|
|
};
|
|
#endif
|
|
|
|
template <typename AllocType>
|
|
static void DeleteDestructor(void* aObj) {
|
|
delete static_cast<AllocType*>(aObj);
|
|
}
|
|
|
|
extern void FreeDestructor(void* aObj);
|
|
|
|
// The ServerCallData is a list of ServerCallItems that should be freed when
|
|
// the server has completed a function call and marshaled a response.
|
|
class ServerCallData {
|
|
public:
|
|
typedef void(DestructorType)(void*);
|
|
|
|
// Allocate a certain type.
|
|
template <typename AllocType>
|
|
AllocType* Allocate(
|
|
DestructorType* aDestructor = &DeleteDestructor<AllocType>) {
|
|
AllocType* ret = new AllocType();
|
|
mList.AppendElement(FreeItem(ret, aDestructor));
|
|
return ret;
|
|
}
|
|
|
|
template <typename AllocType>
|
|
AllocType* Allocate(
|
|
const AllocType& aValueToCopy,
|
|
DestructorType* aDestructor = &DeleteDestructor<AllocType>) {
|
|
AllocType* ret = Allocate<AllocType>(aDestructor);
|
|
*ret = aValueToCopy;
|
|
return ret;
|
|
}
|
|
|
|
// Allocate memory, storing the pointer in buf.
|
|
template <typename PtrType>
|
|
void AllocateMemory(unsigned long aBufLen, PtrType& aBuf) {
|
|
if (aBufLen) {
|
|
aBuf = static_cast<PtrType>(malloc(aBufLen));
|
|
mList.AppendElement(FreeItem(aBuf, FreeDestructor));
|
|
} else {
|
|
aBuf = nullptr;
|
|
}
|
|
}
|
|
|
|
template <typename PtrType>
|
|
void AllocateString(const nsACString& aStr, PtrType& aBuf,
|
|
bool aCopyNullTerminator = true) {
|
|
uint32_t nullByte = aCopyNullTerminator ? 1 : 0;
|
|
char* tempBuf = static_cast<char*>(malloc(aStr.Length() + nullByte));
|
|
memcpy(tempBuf, aStr.Data(), aStr.Length() + nullByte);
|
|
mList.AppendElement(FreeItem(tempBuf, FreeDestructor));
|
|
aBuf = tempBuf;
|
|
}
|
|
|
|
// Run the given destructor on the given memory, for special cases where
|
|
// memory is allocated elsewhere but must still be freed.
|
|
void PostDestructor(void* aMem, DestructorType* aDestructor) {
|
|
mList.AppendElement(FreeItem(aMem, aDestructor));
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
// Allocate memory and a DWORD block-length, storing them in the
|
|
// corresponding parameters.
|
|
template <typename PtrType>
|
|
void AllocateMemory(DWORD aBufLen, PtrType& aBuf, LPDWORD& aBufLenCopy) {
|
|
aBufLenCopy = static_cast<LPDWORD>(malloc(sizeof(DWORD)));
|
|
*aBufLenCopy = aBufLen;
|
|
mList.AppendElement(FreeItem(aBufLenCopy, FreeDestructor));
|
|
AllocateMemory(aBufLen, aBuf);
|
|
}
|
|
#endif // defined(XP_WIN)
|
|
|
|
private:
|
|
// FreeItems are used to free objects that were temporarily needed for
|
|
// dispatch, such as buffers that are given as a parameter.
|
|
class FreeItem {
|
|
void* mPtr;
|
|
DestructorType* mDestructor;
|
|
FreeItem(FreeItem& aOther); // revoked
|
|
public:
|
|
explicit FreeItem(void* aPtr, DestructorType* aDestructor)
|
|
: mPtr(aPtr), mDestructor(aDestructor) {
|
|
MOZ_ASSERT(mDestructor || !aPtr);
|
|
}
|
|
|
|
FreeItem(FreeItem&& aOther)
|
|
: mPtr(aOther.mPtr), mDestructor(aOther.mDestructor) {
|
|
aOther.mPtr = nullptr;
|
|
aOther.mDestructor = nullptr;
|
|
}
|
|
|
|
~FreeItem() {
|
|
if (mDestructor) {
|
|
mDestructor(mPtr);
|
|
}
|
|
}
|
|
};
|
|
|
|
typedef nsTArray<FreeItem> FreeItemList;
|
|
FreeItemList mList;
|
|
};
|
|
|
|
// Holds an IpdlTuple and a ServerCallData. This is used by the phase handlers
|
|
// (RequestHandler and ResponseHandler) in the Unmarshaling phase.
|
|
// Server-side unmarshaling (during the request phase) uses a ServerCallData
|
|
// to keep track of allocated memory. In the client, ServerCallDatas are
|
|
// not used and that value will always be null.
|
|
class IpdlTupleContext {
|
|
public:
|
|
explicit IpdlTupleContext(const IpdlTuple* aTuple,
|
|
ServerCallData* aScd = nullptr)
|
|
: mTuple(aTuple), mScd(aScd) {
|
|
MOZ_ASSERT(aTuple);
|
|
}
|
|
|
|
ServerCallData* GetServerCallData() { return mScd; }
|
|
const IpdlTuple* GetIpdlTuple() { return mTuple; }
|
|
|
|
private:
|
|
const IpdlTuple* mTuple;
|
|
ServerCallData* mScd;
|
|
};
|
|
|
|
template <typename DestType, typename SrcType>
|
|
inline void Copy(DestType& aDest, const SrcType& aSrc) {
|
|
aDest = (DestType)aSrc;
|
|
}
|
|
|
|
template <>
|
|
inline void Copy(nsDependentCSubstring& aDest,
|
|
const nsDependentCSubstring& aSrc) {
|
|
if (aSrc.IsVoid()) {
|
|
aDest.SetIsVoid(true);
|
|
} else {
|
|
aDest.Rebind(aSrc.Data(), aSrc.Length());
|
|
}
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
|
|
template <>
|
|
inline void Copy(uint64_t& aDest, const PTimeStamp& aSrc) {
|
|
aDest = static_cast<uint64_t>(aSrc->QuadPart);
|
|
}
|
|
|
|
template <>
|
|
inline void Copy(PTimeStamp& aDest, const uint64_t& aSrc) {
|
|
aDest->QuadPart = static_cast<LONGLONG>(aSrc);
|
|
}
|
|
|
|
#endif // defined(XP_WIN)
|
|
|
|
template <Endpoint e, typename SelfType>
|
|
struct BaseEndpointHandler;
|
|
template <typename SelfType>
|
|
struct BaseEndpointHandler<CLIENT, SelfType> {
|
|
static const Endpoint OtherSide = SERVER;
|
|
|
|
template <typename DestType, typename SrcType>
|
|
inline static void Copy(ServerCallData* aScd, DestType& aDest,
|
|
const SrcType& aSrc) {
|
|
MOZ_ASSERT(!aScd); // never used in the CLIENT
|
|
SelfType::Copy(aDest, aSrc);
|
|
}
|
|
|
|
template <typename DestType, typename SrcType>
|
|
inline static void Copy(DestType& aDest, const SrcType& aSrc) {
|
|
mozilla::plugins::Copy(aDest, aSrc);
|
|
}
|
|
|
|
// const char* should be null terminated but this is not always the case.
|
|
// In those cases, we must override this default behavior.
|
|
inline static void Copy(nsDependentCSubstring& aDest,
|
|
const char* const& aSrc) {
|
|
// In the client, we just bind to the caller's string
|
|
if (aSrc) {
|
|
aDest.Rebind(aSrc, strlen(aSrc));
|
|
} else {
|
|
aDest.SetIsVoid(true);
|
|
}
|
|
}
|
|
|
|
inline static void Copy(const char*& aDest,
|
|
const nsDependentCSubstring& aSrc) {
|
|
MOZ_ASSERT_UNREACHABLE("Cannot return const parameters.");
|
|
}
|
|
|
|
inline static void Copy(nsDependentCSubstring& aDest, char* const& aSrc) {
|
|
// In the client, we just bind to the caller's string
|
|
if (aSrc) {
|
|
aDest.Rebind(aSrc, strlen(aSrc));
|
|
} else {
|
|
aDest.SetIsVoid(true);
|
|
}
|
|
}
|
|
|
|
inline static void Copy(nsString& aDest, wchar_t* const& aSrc) {
|
|
if (aSrc) {
|
|
// We are using nsString as a "raw" container for a wchar_t string. We
|
|
// just use its data as a wchar_t* later (so the reinterpret_cast is
|
|
// safe).
|
|
aDest.Rebind(reinterpret_cast<char16_t*>(aSrc), wcslen(aSrc));
|
|
} else {
|
|
aDest.SetIsVoid(true);
|
|
}
|
|
}
|
|
|
|
inline static void Copy(char*& aDest, const nsDependentCSubstring& aSrc) {
|
|
MOZ_ASSERT_UNREACHABLE("Returning char* parameters is not yet suported.");
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
inline static void Copy(uint32_t& aDest, const LPDWORD& aSrc) {
|
|
aDest = *aSrc;
|
|
}
|
|
|
|
inline static void Copy(LPDWORD& aDest, const uint32_t& aSrc) {
|
|
*aDest = aSrc;
|
|
}
|
|
#endif // #if defined(XP_WIN)
|
|
};
|
|
|
|
template <typename SelfType>
|
|
struct BaseEndpointHandler<SERVER, SelfType> {
|
|
static const Endpoint OtherSide = CLIENT;
|
|
|
|
// Specializations of this method may allocate memory for types that need it
|
|
// during Unmarshaling. They record the allocation in the ServerCallData.
|
|
// When copying values in the SERVER, we should be sure to carefully validate
|
|
// the information that came from the client as the client may be compromised
|
|
// by malicious code.
|
|
template <typename DestType, typename SrcType>
|
|
inline static void Copy(ServerCallData* aScd, DestType& aDest,
|
|
const SrcType& aSrc) {
|
|
SelfType::Copy(aDest, aSrc);
|
|
}
|
|
|
|
template <typename DestType, typename SrcType>
|
|
inline static void Copy(DestType& aDest, const SrcType& aSrc) {
|
|
mozilla::plugins::Copy(aDest, aSrc);
|
|
}
|
|
|
|
inline static void Copy(nsDependentCSubstring& aDest,
|
|
const nsDependentCSubstring& aSrc) {
|
|
aDest.Rebind(aSrc.Data(), aSrc.Length());
|
|
aDest.SetIsVoid(aSrc.IsVoid());
|
|
}
|
|
|
|
// const char* should be null terminated but this is not always the case.
|
|
// In those cases, we override this default behavior.
|
|
inline static void Copy(nsDependentCSubstring& aDest,
|
|
const char* const& aSrc) {
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
"Const parameter cannot be returned by brokering process.");
|
|
}
|
|
|
|
inline static void Copy(nsDependentCSubstring& aDest, char* const& aSrc) {
|
|
MOZ_ASSERT_UNREACHABLE("Returning char* parameters is not yet suported.");
|
|
}
|
|
|
|
inline static void Copy(ServerCallData* aScd, char*& aDest,
|
|
const nsDependentCSubstring& aSrc) {
|
|
// In the parent, we must allocate the string.
|
|
MOZ_ASSERT(aScd);
|
|
if (aSrc.IsVoid()) {
|
|
aDest = nullptr;
|
|
return;
|
|
}
|
|
aScd->AllocateMemory(aSrc.Length() + 1, aDest);
|
|
memcpy(aDest, aSrc.Data(), aSrc.Length());
|
|
aDest[aSrc.Length()] = '\0';
|
|
}
|
|
|
|
inline static void Copy(ServerCallData* aScd, const char*& aDest,
|
|
const nsDependentCSubstring& aSrc) {
|
|
char* nonConstDest;
|
|
Copy(aScd, nonConstDest, aSrc);
|
|
aDest = nonConstDest;
|
|
}
|
|
|
|
inline static void Copy(ServerCallData* aScd, wchar_t*& aDest,
|
|
const nsString& aSrc) {
|
|
// Allocating the string with aScd means it will last during the server call
|
|
// and be freed when the call is complete.
|
|
MOZ_ASSERT(aScd);
|
|
if (aSrc.IsVoid()) {
|
|
aDest = nullptr;
|
|
return;
|
|
}
|
|
aScd->AllocateMemory((aSrc.Length() + 1) * sizeof(wchar_t), aDest);
|
|
memcpy(aDest, aSrc.Data(), aSrc.Length() * sizeof(wchar_t));
|
|
aDest[aSrc.Length()] = L'\0';
|
|
}
|
|
|
|
inline static void Copy(ServerCallData* aScd, const wchar_t*& aDest,
|
|
const nsString& aSrc) {
|
|
wchar_t* nonConstDest;
|
|
Copy(aScd, nonConstDest, aSrc);
|
|
aDest = nonConstDest;
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
inline static void Copy(uint32_t& aDest, const LPDWORD& aSrc) {
|
|
aDest = *aSrc;
|
|
}
|
|
|
|
inline static void Copy(LPDWORD& aDest, const uint32_t& aSrc) {
|
|
MOZ_RELEASE_ASSERT(aDest);
|
|
*aDest = aSrc;
|
|
}
|
|
|
|
inline static void Copy(ServerCallData* aScd, PTimeStamp& aDest,
|
|
const uint64_t& aSrc) {
|
|
MOZ_ASSERT(!aDest);
|
|
aDest = aScd->Allocate<::TimeStamp>();
|
|
Copy(aDest, aSrc);
|
|
}
|
|
#endif // defined(XP_WIN)
|
|
};
|
|
|
|
// PhaseHandler is a RequestHandler or a ResponseHandler.
|
|
template <Endpoint endpoint, typename PhaseHandler>
|
|
struct Marshaler {
|
|
// Driver
|
|
template <int firstIndex = 0, typename... VarParams>
|
|
static void Marshal(IpdlTuple& aMarshaledTuple, const VarParams&... aParams) {
|
|
MarshalParameters<firstIndex>(aMarshaledTuple, aParams...);
|
|
}
|
|
|
|
// Driver
|
|
template <int firstIndex = 0, typename... VarParams>
|
|
static bool Unmarshal(IpdlTupleContext& aUnmarshaledTuple,
|
|
VarParams&... aParams) {
|
|
return UnmarshalParameters<firstIndex>(aUnmarshaledTuple, 0, aParams...);
|
|
}
|
|
|
|
template <int paramIndex, typename OrigType,
|
|
bool shouldMarshal =
|
|
PhaseHandler::Info::template ShouldMarshal<paramIndex>::value>
|
|
struct MaybeMarshalParameter {};
|
|
|
|
/**
|
|
* shouldMarshal = true case
|
|
*/
|
|
template <int paramIndex, typename OrigType>
|
|
struct MaybeMarshalParameter<paramIndex, OrigType, true> {
|
|
template <typename IPCType = typename PhaseHandler::template IPCTypeMap<
|
|
OrigType>::ipc_type>
|
|
static void MarshalParameter(IpdlTuple& aMarshaledTuple,
|
|
const OrigType& aParam) {
|
|
HOOK_LOG(LogLevel::Verbose, ("%s marshaling parameter %d.",
|
|
EndpointMsg(endpoint), paramIndex));
|
|
IPCType ipcObject;
|
|
// EndpointHandler must be able to Copy() from OrigType to IPCType
|
|
PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(
|
|
ipcObject, aParam);
|
|
LogParameterValue(paramIndex, ipcObject);
|
|
aMarshaledTuple.AddElement(ipcObject);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* shouldMarshal = false case
|
|
*/
|
|
template <int paramIndex, typename OrigType>
|
|
struct MaybeMarshalParameter<paramIndex, OrigType, false> {
|
|
static void MarshalParameter(IpdlTuple& aMarshaledTuple,
|
|
const OrigType& aParam) {
|
|
HOOK_LOG(LogLevel::Verbose, ("%s not marshaling parameter %d.",
|
|
EndpointMsg(endpoint), paramIndex));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Recursive case: marshals aFirstParam to aMarshaledTuple (if desired),
|
|
* then marshals the aRemainingParams.
|
|
*/
|
|
template <int paramIndex, typename VarParam, typename... VarParams>
|
|
static void MarshalParameters(IpdlTuple& aMarshaledTuple,
|
|
const VarParam& aFirstParam,
|
|
const VarParams&... aRemainingParams) {
|
|
MaybeMarshalParameter<paramIndex, VarParam>::MarshalParameter(
|
|
aMarshaledTuple, aFirstParam);
|
|
MarshalParameters<paramIndex + 1, VarParams...>(aMarshaledTuple,
|
|
aRemainingParams...);
|
|
}
|
|
|
|
/**
|
|
* Base case: empty parameter list -- nothing to marshal.
|
|
*/
|
|
template <int paramIndex>
|
|
static void MarshalParameters(IpdlTuple& aMarshaledTuple) {}
|
|
|
|
template <int tupleIndex, typename OrigType,
|
|
bool shouldMarshal =
|
|
PhaseHandler::Info::template ShouldMarshal<tupleIndex>::value,
|
|
bool hasFixedValue =
|
|
PhaseHandler::Info::template HasFixedValue<tupleIndex>::value>
|
|
struct MaybeUnmarshalParameter {};
|
|
|
|
/**
|
|
* ShouldMarshal = true case. HasFixedValue must be false in that case.
|
|
*/
|
|
template <int tupleIndex, typename VarParam>
|
|
struct MaybeUnmarshalParameter<tupleIndex, VarParam, true, false> {
|
|
template <typename IPCType = typename PhaseHandler::template IPCTypeMap<
|
|
VarParam>::ipc_type>
|
|
static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
|
|
int& aNextTupleIdx,
|
|
VarParam& aParam) {
|
|
const IPCType* ipcObject =
|
|
aUnmarshaledTuple.GetIpdlTuple()->Element<IPCType>(aNextTupleIdx);
|
|
if (!ipcObject) {
|
|
HOOK_LOG(LogLevel::Error, ("%s failed to unmarshal parameter %d.",
|
|
EndpointMsg(endpoint), tupleIndex));
|
|
return false;
|
|
}
|
|
HOOK_LOG(LogLevel::Verbose, ("%s unmarshaled parameter %d.",
|
|
EndpointMsg(endpoint), tupleIndex));
|
|
LogParameterValue(tupleIndex, *ipcObject);
|
|
PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(
|
|
aUnmarshaledTuple.GetServerCallData(), aParam, *ipcObject);
|
|
++aNextTupleIdx;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ShouldMarshal = true : nsDependentCSubstring specialization
|
|
*/
|
|
template <int tupleIndex>
|
|
struct MaybeUnmarshalParameter<tupleIndex, nsDependentCSubstring, true,
|
|
false> {
|
|
static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
|
|
int& aNextTupleIdx,
|
|
nsDependentCSubstring& aParam) {
|
|
// Deserialize as an nsCString and then copy the info into the
|
|
// nsDependentCSubstring
|
|
const nsCString* ipcObject =
|
|
aUnmarshaledTuple.GetIpdlTuple()->Element<nsCString>(aNextTupleIdx);
|
|
if (!ipcObject) {
|
|
HOOK_LOG(LogLevel::Error, ("%s failed to unmarshal parameter %d.",
|
|
EndpointMsg(endpoint), tupleIndex));
|
|
return false;
|
|
}
|
|
HOOK_LOG(LogLevel::Verbose, ("%s unmarshaled parameter %d.",
|
|
EndpointMsg(endpoint), tupleIndex));
|
|
|
|
aParam.Rebind(ipcObject->Data(), ipcObject->Length());
|
|
aParam.SetIsVoid(ipcObject->IsVoid());
|
|
LogParameterValue(tupleIndex, aParam);
|
|
++aNextTupleIdx;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ShouldMarshal = true : char* specialization
|
|
*/
|
|
template <int tupleIndex>
|
|
struct MaybeUnmarshalParameter<tupleIndex, char*, true, false> {
|
|
static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
|
|
int& aNextTupleIdx, char*& aParam) {
|
|
nsDependentCSubstring tempStr;
|
|
bool ret =
|
|
MaybeUnmarshalParameter<tupleIndex, nsDependentCSubstring, true,
|
|
false>::UnmarshalParameter(aUnmarshaledTuple,
|
|
aNextTupleIdx,
|
|
tempStr);
|
|
PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(
|
|
aUnmarshaledTuple.GetServerCallData(), aParam, tempStr);
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ShouldMarshal = true : const char* specialization
|
|
*/
|
|
template <int tupleIndex>
|
|
struct MaybeUnmarshalParameter<tupleIndex, const char*, true, false> {
|
|
static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
|
|
int& aNextTupleIdx,
|
|
const char*& aParam) {
|
|
char* tempStr;
|
|
bool ret =
|
|
MaybeUnmarshalParameter<tupleIndex, char*, true,
|
|
false>::UnmarshalParameter(aUnmarshaledTuple,
|
|
aNextTupleIdx,
|
|
tempStr);
|
|
aParam = tempStr;
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ShouldMarshal = false, fixed parameter case
|
|
*/
|
|
template <int tupleIndex, typename VarParam>
|
|
struct MaybeUnmarshalParameter<tupleIndex, VarParam, false, true> {
|
|
static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
|
|
int& aNextTupleIdx,
|
|
VarParam& aParam) {
|
|
// Copy default value if this is client->server communication (and if it
|
|
// exists)
|
|
PhaseHandler::template CopyFixedParam<tupleIndex, VarParam>(aParam);
|
|
HOOK_LOG(LogLevel::Verbose,
|
|
("%s parameter %d not unmarshaling -- using fixed value.",
|
|
EndpointMsg(endpoint), tupleIndex));
|
|
LogParameterValue(tupleIndex, aParam);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ShouldMarshal = false, unfixed parameter case. Assume user has done
|
|
* special handling.
|
|
*/
|
|
template <int tupleIndex, typename VarParam>
|
|
struct MaybeUnmarshalParameter<tupleIndex, VarParam, false, false> {
|
|
static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
|
|
int& aNextTupleIdx,
|
|
VarParam& aParam) {
|
|
HOOK_LOG(LogLevel::Verbose,
|
|
("%s parameter %d not automatically unmarshaling.",
|
|
EndpointMsg(endpoint), tupleIndex));
|
|
// DLP: TODO: specializations fail LogParameterValue(tupleIndex, aParam);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Recursive case: unmarshals aFirstParam to aUnmarshaledTuple (if desired),
|
|
* then unmarshals the aRemainingParams.
|
|
* The endpoint specifies the side this process is on: client or server.
|
|
*/
|
|
template <int tupleIndex, typename VarParam, typename... VarParams>
|
|
static bool UnmarshalParameters(IpdlTupleContext& aUnmarshaledTuple,
|
|
int aNextTupleIdx, VarParam& aFirstParam,
|
|
VarParams&... aRemainingParams) {
|
|
// TODO: DLP: I currently increment aNextTupleIdx in the method (its a
|
|
// reference). This is awful.
|
|
if (!MaybeUnmarshalParameter<tupleIndex, VarParam>::UnmarshalParameter(
|
|
aUnmarshaledTuple, aNextTupleIdx, aFirstParam)) {
|
|
return false;
|
|
}
|
|
return UnmarshalParameters<tupleIndex + 1, VarParams...>(
|
|
aUnmarshaledTuple, aNextTupleIdx, aRemainingParams...);
|
|
}
|
|
|
|
/**
|
|
* Base case: empty parameter list -- nothing to unmarshal.
|
|
*/
|
|
template <int>
|
|
static bool UnmarshalParameters(IpdlTupleContext& aUnmarshaledTuple,
|
|
int aNextTupleIdx) {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// The default marshals all parameters.
|
|
template <FunctionHookId functionId>
|
|
struct RequestInfo {
|
|
template <int paramIndex>
|
|
struct FixedValue;
|
|
|
|
template <int paramIndex, typename = int>
|
|
struct HasFixedValue {
|
|
static const bool value = false;
|
|
};
|
|
template <int paramIndex>
|
|
struct HasFixedValue<paramIndex, decltype(FixedValue<paramIndex>::value, 0)> {
|
|
static const bool value = true;
|
|
};
|
|
|
|
// By default we the request should marshal any non-fixed parameters.
|
|
template <int paramIndex>
|
|
struct ShouldMarshal {
|
|
static const bool value = !HasFixedValue<paramIndex>::value;
|
|
};
|
|
};
|
|
|
|
/**
|
|
* This base stores the RequestHandler's IPCTypeMap. It really only
|
|
* exists to circumvent the arbitrary C++ rule (enforced by mingw) forbidding
|
|
* full class specialization of a class (IPCTypeMap<T>) inside of an
|
|
* unspecialized template class (RequestHandler<T>).
|
|
*/
|
|
struct RequestHandlerBase {
|
|
// Default to the namespace-level IPCTypeMap
|
|
template <typename OrigType>
|
|
struct IPCTypeMap {
|
|
typedef typename mozilla::plugins::IPCTypeMap<OrigType>::ipc_type ipc_type;
|
|
};
|
|
};
|
|
|
|
#if defined(XP_WIN)
|
|
|
|
// Request phase uses OpenFileNameIPC for an LPOPENFILENAMEW parameter.
|
|
template <>
|
|
struct RequestHandlerBase::IPCTypeMap<LPOPENFILENAMEW> {
|
|
typedef OpenFileNameIPC ipc_type;
|
|
};
|
|
|
|
#endif // defined(XP_WIN)
|
|
|
|
struct BaseEHContainer {
|
|
template <Endpoint e>
|
|
struct EndpointHandler : public BaseEndpointHandler<e, EndpointHandler<e>> {};
|
|
};
|
|
|
|
template <FunctionHookId functionId, typename FunctionType,
|
|
typename EHContainer>
|
|
struct RequestHandler;
|
|
|
|
template <FunctionHookId functionId, typename EHContainerType,
|
|
typename ResultType, typename... ParamTypes>
|
|
struct RequestHandler<functionId, ResultType HOOK_CALL(ParamTypes...),
|
|
EHContainerType> : public RequestHandlerBase {
|
|
typedef ResultType(HOOK_CALL FunctionType)(ParamTypes...);
|
|
typedef RequestHandler<functionId, FunctionType, EHContainerType> SelfType;
|
|
typedef RequestInfo<functionId> Info;
|
|
typedef EHContainerType EHContainer;
|
|
|
|
static void Marshal(IpdlTuple& aTuple, const ParamTypes&... aParams) {
|
|
ReqMarshaler::Marshal(aTuple, aParams...);
|
|
}
|
|
|
|
static bool Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple,
|
|
ParamTypes&... aParams) {
|
|
IpdlTupleContext cxt(&aTuple, &aScd);
|
|
return ReqUnmarshaler::Unmarshal(cxt, aParams...);
|
|
}
|
|
|
|
typedef Marshaler<CLIENT, SelfType> ReqMarshaler;
|
|
typedef Marshaler<SERVER, SelfType> ReqUnmarshaler;
|
|
|
|
/**
|
|
* Returns true if a call made with the given parameters should be
|
|
* brokered (vs. passed-through to the original function).
|
|
*/
|
|
static bool ShouldBroker(Endpoint aEndpoint, const ParamTypes&... aParams) {
|
|
// True if all filtered parameters match their filter value.
|
|
return CheckFixedParams(aParams...);
|
|
}
|
|
|
|
template <int paramIndex, typename VarParam>
|
|
static void CopyFixedParam(VarParam& aParam) {
|
|
aParam = Info::template FixedValue<paramIndex>::value;
|
|
}
|
|
|
|
protected:
|
|
// Returns true if filtered parameters match their filter value.
|
|
static bool CheckFixedParams(const ParamTypes&... aParams) {
|
|
return CheckFixedParamsHelper<0>(aParams...);
|
|
}
|
|
|
|
// If no FixedValue<paramIndex> is defined and equal to FixedType then always
|
|
// pass.
|
|
template <int paramIndex, typename = int>
|
|
struct CheckFixedParam {
|
|
template <typename ParamType>
|
|
static inline bool Check(const ParamType& aParam) {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// If FixedValue<paramIndex> is defined then check equality.
|
|
template <int paramIndex>
|
|
struct CheckFixedParam<
|
|
paramIndex, decltype(Info::template FixedValue<paramIndex>::value, 0)> {
|
|
template <typename ParamType>
|
|
static inline bool Check(ParamType& aParam) {
|
|
return ParameterEquality(aParam,
|
|
Info::template FixedValue<paramIndex>::value);
|
|
}
|
|
};
|
|
|
|
// Recursive case: Chcek head parameter, then tail parameters.
|
|
template <int index, typename VarParam, typename... VarParams>
|
|
static bool CheckFixedParamsHelper(const VarParam& aParam,
|
|
const VarParams&... aParams) {
|
|
if (!CheckFixedParam<index>::Check(aParam)) {
|
|
return false; // didn't match a fixed parameter
|
|
}
|
|
return CheckFixedParamsHelper<index + 1>(aParams...);
|
|
}
|
|
|
|
// Base case: All fixed parameters matched.
|
|
template <int>
|
|
static bool CheckFixedParamsHelper() {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// The default returns no parameters -- only the return value.
|
|
template <FunctionHookId functionId>
|
|
struct ResponseInfo {
|
|
template <int paramIndex>
|
|
struct HasFixedValue {
|
|
static const bool value =
|
|
RequestInfo<functionId>::template HasFixedValue<paramIndex>::value;
|
|
};
|
|
|
|
// Only the return value (index -1) is sent by default.
|
|
template <int paramIndex>
|
|
struct ShouldMarshal {
|
|
static const bool value = (paramIndex == -1);
|
|
};
|
|
|
|
// This is the condition on the function result that we use to determine if
|
|
// the windows thread-local error state should be sent to the client. The
|
|
// error is typically only relevant if the function did not succeed.
|
|
template <typename ResultType>
|
|
static bool ShouldTransmitError(const ResultType& aResult) {
|
|
return !static_cast<bool>(aResult);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Same rationale as for RequestHandlerBase.
|
|
*/
|
|
struct ResponseHandlerBase {
|
|
// Default to the namespace-level IPCTypeMap
|
|
template <typename OrigType>
|
|
struct IPCTypeMap {
|
|
typedef typename mozilla::plugins::IPCTypeMap<OrigType>::ipc_type ipc_type;
|
|
};
|
|
};
|
|
|
|
#if defined(XP_WIN)
|
|
|
|
// Response phase uses OpenFileNameRetIPC for an LPOPENFILENAMEW parameter.
|
|
template <>
|
|
struct ResponseHandlerBase::IPCTypeMap<LPOPENFILENAMEW> {
|
|
typedef OpenFileNameRetIPC ipc_type;
|
|
};
|
|
|
|
#endif
|
|
|
|
template <FunctionHookId functionId, typename FunctionType,
|
|
typename EHContainer>
|
|
struct ResponseHandler;
|
|
|
|
template <FunctionHookId functionId, typename EHContainerType,
|
|
typename ResultType, typename... ParamTypes>
|
|
struct ResponseHandler<functionId, ResultType HOOK_CALL(ParamTypes...),
|
|
EHContainerType> : public ResponseHandlerBase {
|
|
typedef ResultType(HOOK_CALL FunctionType)(ParamTypes...);
|
|
typedef ResponseHandler<functionId, FunctionType, EHContainerType> SelfType;
|
|
typedef ResponseInfo<functionId> Info;
|
|
typedef EHContainerType EHContainer;
|
|
|
|
static void Marshal(IpdlTuple& aTuple, const ResultType& aResult,
|
|
const ParamTypes&... aParams) {
|
|
// Note that this "trick" means that the first parameter we marshal is
|
|
// considered to be parameter #-1 when checking the ResponseInfo.
|
|
// The parameters in the list therefore start at index 0.
|
|
RspMarshaler::template Marshal<-1>(aTuple, aResult, aParams...);
|
|
}
|
|
static bool Unmarshal(const IpdlTuple& aTuple, ResultType& aResult,
|
|
ParamTypes&... aParams) {
|
|
IpdlTupleContext cxt(&aTuple);
|
|
return RspUnmarshaler::template Unmarshal<-1>(cxt, aResult, aParams...);
|
|
}
|
|
|
|
typedef Marshaler<SERVER, SelfType> RspMarshaler;
|
|
typedef Marshaler<CLIENT, SelfType> RspUnmarshaler;
|
|
|
|
// Fixed parameters are not used in the response phase.
|
|
template <int tupleIndex, typename VarParam>
|
|
static void CopyFixedParam(VarParam& aParam) {}
|
|
};
|
|
|
|
/**
|
|
* Reference-counted monitor, used to synchronize communication between a
|
|
* thread using a brokered API and the FunctionDispatch thread.
|
|
*/
|
|
class FDMonitor : public Monitor {
|
|
public:
|
|
FDMonitor() : Monitor("FunctionDispatchThread lock") {}
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FDMonitor)
|
|
|
|
private:
|
|
~FDMonitor() {}
|
|
};
|
|
|
|
/**
|
|
* Data for hooking a function that we automatically broker in a remote
|
|
* process.
|
|
*/
|
|
template <FunctionHookId functionId, typename FunctionType,
|
|
typename EHContainer = BaseEHContainer>
|
|
class FunctionBroker;
|
|
|
|
template <FunctionHookId functionId, typename EHContainer, typename ResultType,
|
|
typename... ParamTypes>
|
|
class FunctionBroker<functionId, ResultType HOOK_CALL(ParamTypes...),
|
|
EHContainer>
|
|
: public BasicFunctionHook<functionId,
|
|
ResultType HOOK_CALL(ParamTypes...)> {
|
|
public:
|
|
typedef Tuple<ParamTypes...> TupleParamTypes;
|
|
typedef Tuple<mozilla::Maybe<ParamTypes>...> TupleMaybeParamTypes;
|
|
typedef Tuple<ParamTypes*...> TupleParamPtrTypes;
|
|
typedef Tuple<ParamTypes&...> TupleParamRefTypes;
|
|
static const size_t numParams = sizeof...(ParamTypes);
|
|
|
|
typedef ResultType(HOOK_CALL FunctionType)(ParamTypes...);
|
|
typedef FunctionBroker<functionId, FunctionType, EHContainer> SelfType;
|
|
typedef BasicFunctionHook<functionId, FunctionType> FunctionHookInfoType;
|
|
typedef FunctionHookInfoType BaseType;
|
|
|
|
typedef RequestHandler<functionId, FunctionType, EHContainer> Request;
|
|
typedef ResponseHandler<functionId, FunctionType, EHContainer> Response;
|
|
|
|
template <typename DelegateFcnType>
|
|
using RequestDelegate =
|
|
RequestHandler<functionId, DelegateFcnType, EHContainer>;
|
|
template <typename DelegateFcnType>
|
|
using ResponseDelegate =
|
|
ResponseHandler<functionId, DelegateFcnType, EHContainer>;
|
|
|
|
FunctionBroker(const char* aModuleName, const char* aMethodName,
|
|
FunctionType* aOriginalFunction)
|
|
: BasicFunctionHook<functionId, FunctionType>(
|
|
aModuleName, aMethodName, aOriginalFunction, InterceptorStub) {}
|
|
|
|
// This is the function used to replace the original DLL-intercepted function.
|
|
static ResultType HOOK_CALL InterceptorStub(ParamTypes... aParams) {
|
|
MOZ_ASSERT(functionId < FunctionHook::GetHooks()->Length());
|
|
FunctionHook* self = FunctionHook::GetHooks()->ElementAt(functionId);
|
|
MOZ_ASSERT(self && self->FunctionId() == functionId);
|
|
const SelfType* broker = static_cast<const SelfType*>(self);
|
|
return broker->MaybeBrokerCallClient(aParams...);
|
|
}
|
|
|
|
/**
|
|
* Handle a call by running the original version or brokering, depending on
|
|
* ShouldBroker. All parameter types (including the result type)
|
|
* must have IPDL ParamTraits specializations or appear in this object's
|
|
* IPCTypeMap. If brokering fails for any reason then this falls back to
|
|
* calling the original version of the function.
|
|
*/
|
|
ResultType MaybeBrokerCallClient(ParamTypes&... aParameters) const;
|
|
|
|
/**
|
|
* Called server-side to run the original function using aInTuple
|
|
* as parameter values. The return value and returned parameters
|
|
* (in that order) are added to aOutTuple.
|
|
*/
|
|
bool RunOriginalFunction(base::ProcessId aClientId,
|
|
const IPC::IpdlTuple& aInTuple,
|
|
IPC::IpdlTuple* aOutTuple) const override {
|
|
return BrokerCallServer(aClientId, aInTuple, aOutTuple);
|
|
}
|
|
|
|
protected:
|
|
bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple& aInTuple,
|
|
IpdlTuple* aOutTuple) const {
|
|
return BrokerCallServer(aClientId, aInTuple, aOutTuple,
|
|
std::index_sequence_for<ParamTypes...>{});
|
|
}
|
|
|
|
bool BrokerCallClient(uint32_t& aWinError, ResultType& aResult,
|
|
ParamTypes&... aParameters) const;
|
|
bool PostToDispatchThread(uint32_t& aWinError, ResultType& aRet,
|
|
ParamTypes&... aParameters) const;
|
|
|
|
static void PostToDispatchHelper(const SelfType* bmhi,
|
|
RefPtr<FDMonitor> monitor, bool* notified,
|
|
bool* ok, uint32_t* winErr, ResultType* r,
|
|
ParamTypes*... p) {
|
|
// Note: p is also non-null... its just hard to assert that.
|
|
MOZ_ASSERT(bmhi && monitor && notified && ok && winErr && r);
|
|
MOZ_ASSERT(*notified == false);
|
|
*ok = bmhi->BrokerCallClient(*winErr, *r, *p...);
|
|
|
|
{
|
|
// We need to grab the lock to make sure that Wait() has been
|
|
// called in PostToDispatchThread. We need that since we wake it with
|
|
// Notify().
|
|
MonitorAutoLock lock(*monitor);
|
|
*notified = true;
|
|
}
|
|
|
|
monitor->Notify();
|
|
};
|
|
|
|
template <typename... VarParams>
|
|
ResultType RunFunction(FunctionType* aFunction, base::ProcessId aClientId,
|
|
VarParams&... aParams) const {
|
|
return aFunction(aParams...);
|
|
};
|
|
|
|
bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple& aInTuple,
|
|
IpdlTuple* aOutTuple, ParamTypes&... aParams) const;
|
|
|
|
template <size_t... Indices>
|
|
bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple& aInTuple,
|
|
IpdlTuple* aOutTuple,
|
|
std::index_sequence<Indices...>) const {
|
|
TupleParamTypes paramTuple;
|
|
return BrokerCallServer(aClientId, aInTuple, aOutTuple,
|
|
Get<Indices>(paramTuple)...);
|
|
}
|
|
};
|
|
|
|
template <FunctionHookId functionId, typename EHContainer, typename ResultType,
|
|
typename... ParamTypes>
|
|
ResultType FunctionBroker<
|
|
functionId, ResultType HOOK_CALL(ParamTypes...),
|
|
EHContainer>::MaybeBrokerCallClient(ParamTypes&... aParameters) const {
|
|
MOZ_ASSERT(FunctionBrokerChild::GetInstance());
|
|
|
|
// Broker the call if ShouldBroker says to. Otherwise, or if brokering
|
|
// fails, then call the original implementation.
|
|
if (!FunctionBrokerChild::GetInstance()) {
|
|
HOOK_LOG(LogLevel::Error,
|
|
("[%s] Client attempted to broker call without actor.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
} else if (Request::ShouldBroker(CLIENT, aParameters...)) {
|
|
HOOK_LOG(LogLevel::Debug, ("[%s] Client attempting to broker call.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
uint32_t winError;
|
|
ResultType ret;
|
|
bool success = BrokerCallClient(winError, ret, aParameters...);
|
|
HOOK_LOG(LogLevel::Info,
|
|
("[%s] Client brokering %s.",
|
|
FunctionHookInfoType::mFunctionName.Data(), SuccessMsg(success)));
|
|
if (success) {
|
|
#if defined(XP_WIN)
|
|
if (Response::Info::ShouldTransmitError(ret)) {
|
|
HOOK_LOG(LogLevel::Debug,
|
|
("[%s] Client setting thread error code: %08x.",
|
|
FunctionHookInfoType::mFunctionName.Data(), winError));
|
|
::SetLastError(winError);
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
HOOK_LOG(LogLevel::Info,
|
|
("[%s] Client could not broker. Running original version.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
return FunctionHookInfoType::mOldFunction(aParameters...);
|
|
}
|
|
|
|
template <FunctionHookId functionId, typename EHContainer, typename ResultType,
|
|
typename... ParamTypes>
|
|
bool FunctionBroker<functionId, ResultType HOOK_CALL(ParamTypes...),
|
|
EHContainer>::BrokerCallClient(uint32_t& aWinError,
|
|
ResultType& aResult,
|
|
ParamTypes&... aParameters)
|
|
const {
|
|
if (!FunctionBrokerChild::GetInstance()->IsDispatchThread()) {
|
|
return PostToDispatchThread(aWinError, aResult, aParameters...);
|
|
}
|
|
|
|
if (FunctionBrokerChild::GetInstance()) {
|
|
IpdlTuple sending, returned;
|
|
HOOK_LOG(LogLevel::Debug, ("[%s] Client marshaling parameters.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
Request::Marshal(sending, aParameters...);
|
|
HOOK_LOG(LogLevel::Info, ("[%s] Client sending broker message.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
if (FunctionBrokerChild::GetInstance()->SendBrokerFunction(
|
|
FunctionHookInfoType::FunctionId(), sending, &returned)) {
|
|
HOOK_LOG(LogLevel::Debug,
|
|
("[%s] Client received broker message response.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
bool success = Response::Unmarshal(returned, aResult, aParameters...);
|
|
HOOK_LOG(LogLevel::Info, ("[%s] Client response unmarshaling: %s.",
|
|
FunctionHookInfoType::mFunctionName.Data(),
|
|
SuccessMsg(success)));
|
|
#if defined(XP_WIN)
|
|
if (success && Response::Info::ShouldTransmitError(aResult)) {
|
|
uint32_t* winError =
|
|
returned.Element<UINT32>(returned.NumElements() - 1);
|
|
if (!winError) {
|
|
HOOK_LOG(LogLevel::Error,
|
|
("[%s] Client failed to unmarshal error code.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
return false;
|
|
}
|
|
HOOK_LOG(LogLevel::Debug,
|
|
("[%s] Client response unmarshaled error code: %08x.",
|
|
FunctionHookInfoType::mFunctionName.Data(), *winError));
|
|
aWinError = *winError;
|
|
}
|
|
#endif
|
|
return success;
|
|
}
|
|
}
|
|
|
|
HOOK_LOG(LogLevel::Error, ("[%s] Client failed to broker call.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
return false;
|
|
}
|
|
|
|
template <FunctionHookId functionId, typename EHContainer, typename ResultType,
|
|
typename... ParamTypes>
|
|
bool FunctionBroker<functionId, ResultType HOOK_CALL(ParamTypes...),
|
|
EHContainer>::BrokerCallServer(base::ProcessId aClientId,
|
|
const IpdlTuple& aInTuple,
|
|
IpdlTuple* aOutTuple,
|
|
ParamTypes&... aParams)
|
|
const {
|
|
HOOK_LOG(LogLevel::Info, ("[%s] Server brokering function.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
|
|
ServerCallData scd;
|
|
if (!Request::Unmarshal(scd, aInTuple, aParams...)) {
|
|
HOOK_LOG(LogLevel::Info, ("[%s] Server failed to unmarshal.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
return false;
|
|
}
|
|
|
|
// Make sure that this call was legal -- do not execute a call that
|
|
// shouldn't have been brokered in the first place.
|
|
if (!Request::ShouldBroker(SERVER, aParams...)) {
|
|
HOOK_LOG(LogLevel::Error, ("[%s] Server rejected brokering request.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
return false;
|
|
}
|
|
|
|
// Run the function we are brokering.
|
|
HOOK_LOG(LogLevel::Info, ("[%s] Server broker running function.",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
ResultType ret =
|
|
RunFunction(FunctionHookInfoType::mOldFunction, aClientId, aParams...);
|
|
|
|
#if defined(XP_WIN)
|
|
// Record the thread-local error state (before it is changed) if needed.
|
|
uint32_t err = UINT_MAX;
|
|
bool transmitError = Response::Info::ShouldTransmitError(ret);
|
|
if (transmitError) {
|
|
err = ::GetLastError();
|
|
HOOK_LOG(LogLevel::Info, ("[%s] Server returning thread error code: %08x.",
|
|
FunctionHookInfoType::mFunctionName.Data(), err));
|
|
}
|
|
#endif
|
|
|
|
// Add the result, win thread error and any returned parameters to the
|
|
// returned tuple.
|
|
Response::Marshal(*aOutTuple, ret, aParams...);
|
|
#if defined(XP_WIN)
|
|
if (transmitError) {
|
|
aOutTuple->AddElement(err);
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
template <FunctionHookId functionId, typename EHContainer, typename ResultType,
|
|
typename... ParamTypes>
|
|
bool FunctionBroker<
|
|
functionId, ResultType HOOK_CALL(ParamTypes...),
|
|
EHContainer>::PostToDispatchThread(uint32_t& aWinError, ResultType& aRet,
|
|
ParamTypes&... aParameters) const {
|
|
MOZ_ASSERT(!FunctionBrokerChild::GetInstance()->IsDispatchThread());
|
|
HOOK_LOG(LogLevel::Debug, ("Posting broker task '%s' to dispatch thread",
|
|
FunctionHookInfoType::mFunctionName.Data()));
|
|
|
|
// Run PostToDispatchHelper on the dispatch thread. It will notify our
|
|
// waiting monitor when it is done.
|
|
RefPtr<FDMonitor> monitor(new FDMonitor());
|
|
MonitorAutoLock lock(*monitor);
|
|
bool success = false;
|
|
bool notified = false;
|
|
FunctionBrokerChild::GetInstance()->PostToDispatchThread(NewRunnableFunction(
|
|
"FunctionDispatchThreadRunnable", &PostToDispatchHelper, this, monitor,
|
|
¬ified, &success, &aWinError, &aRet, &aParameters...));
|
|
|
|
// We wait to be notified, testing that notified was actually set to make
|
|
// sure this isn't a spurious wakeup.
|
|
while (!notified) {
|
|
monitor->Wait();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
void AddBrokeredFunctionHooks(FunctionHookArray& aHooks);
|
|
|
|
} // namespace plugins
|
|
} // namespace mozilla
|
|
|
|
#endif // dom_plugins_ipc_PluginHooksWin_h
|