summaryrefslogtreecommitdiffstats
path: root/dom/plugins/ipc/FunctionBroker.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/plugins/ipc/FunctionBroker.h1452
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, &paramString);
+ 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,
+ &notified, &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