diff options
Diffstat (limited to '')
-rw-r--r-- | dom/plugins/ipc/FunctionBroker.h | 1452 |
1 files changed, 1452 insertions, 0 deletions
diff --git a/dom/plugins/ipc/FunctionBroker.h b/dom/plugins/ipc/FunctionBroker.h new file mode 100644 index 0000000000..ddbde631e3 --- /dev/null +++ b/dom/plugins/ipc/FunctionBroker.h @@ -0,0 +1,1452 @@ +/* -*- 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 "transport/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. + * + */ + +#if defined(XP_WIN) && defined(__clang__) +# if __has_declspec_attribute(guard) +// Workaround for https://bugs.llvm.org/show_bug.cgi?id=47617 +// Some of the brokered function thunks don't get properly marked as call +// targets, so we have to disable CFG when returning to the original function. +# define BROKER_DISABLE_CFGUARD __declspec(guard(nocf)) +# else +# define BROKER_DISABLE_CFGUARD /* nothing */ +# endif +#else +# define BROKER_DISABLE_CFGUARD /* nothing */ +#endif + +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() = default; +}; + +/** + * 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> + BROKER_DISABLE_CFGUARD 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 |