Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/plugins/ipc/FunctionBroker.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef dom_plugins_ipc_PluginHooksWin_h
8
#define dom_plugins_ipc_PluginHooksWin_h 1
9
10
#include <map>
11
#include <algorithm>
12
#include <utility>
13
#include "base/task.h"
14
#include "mozilla/ipc/ProcessChild.h"
15
#include "FunctionBrokerChild.h"
16
#include "mtransport/runnable_utils.h"
17
#include "PluginMessageUtils.h"
18
#include "mozilla/Logging.h"
19
#include "FunctionHook.h"
20
#include "FunctionBrokerIPCUtils.h"
21
22
#if defined(XP_WIN)
23
#define SECURITY_WIN32
24
#include <security.h>
25
#include <wininet.h>
26
#include <schnlsp.h>
27
#if defined(MOZ_SANDBOX)
28
#include "sandboxPermissions.h"
29
#endif
30
#endif // defined(XP_WIN)
31
32
/**
33
 * This functionality supports automatic method hooking (FunctionHook) and
34
 * brokering (FunctionBroker), which are used to intercept system calls
35
 * (using the nsDllInterceptor) and replace them with new functionality (hook)
36
 * or proxy them on another process (broker).
37
 * There isn't much of a public interface to this (see FunctionHook
38
 * for initialization functionality) since the majority of the behavior
39
 * comes from intercepting calls to DLL methods (making those DLL methods the
40
 * public interface).  Generic RPC can be achieved without DLLs or function
41
 * interception by directly calling the FunctionBroker::InterceptorStub.
42
 *
43
 * The system supports the most common logic surrounding brokering by allowing
44
 * the client to supply strategies for them.  Some examples of common tasks that
45
 * are supported by automatic brokering:
46
 *
47
 * * Intercepting a new Win32 method:
48
 *
49
 * Step 1: Add a typedef or subclass of either FunctionHook (non-brokering) or
50
 * FunctionBroker (automatic brokering) to FunctionBroker.cpp, using a new
51
 * FunctionHookID (added to that enum).
52
 * For example:
53
 *   typedef FunctionBroker<ID_GetKeyState, decltype(GetKeyState)> GetKeyStateFB
54
 * Use a subclass instead of a typedef if you need to maintain data or state.
55
 *
56
 * Step 2: Add an instance of that object to the FunctionHookList in
57
 * AddFunctionHook(FunctionHookList&) or AddBrokeredFunctionHook(FunctionHookList&).
58
 * This typically just means calling the constructor with the correct info.
59
 * At a minimum, this means supplying the names of the DLL and method to
60
 * broker, and a pointer to the original version of the method.
61
 * For example:
62
 *   aHooks[ID_GetKeyState] =
63
 *     new GetKeyStateFB("user32.dll", "GetKeyState", &GetKeyState);
64
 *
65
 * Step 3: If brokering, make sure the system can (un)marshal the parameters,
66
 * either by the means below or by adding the type to IpdlTuple, which we use
67
 * for type-safely (un)marshaling the parameter list.
68
 *
69
 * * Only brokering _some_ calls to the method:
70
 *
71
 * FunctionBroker's constructor allows the user to supply a ShouldBroker
72
 * function, which takes the parameters of the method call and returns false
73
 * if we should use the original method instead of brokering.
74
 *
75
 * * Only passing _some_ parameters to the brokering process / returning
76
 *   parameters to client:
77
 *
78
 * If a system call changes a parameter call-by-reference style then the
79
 * parameter's value needs to be returned to the client.  The FunctionBroker
80
 * has "phase" (request/response) objects that it uses to determine which
81
 * parameters are sent/returned.  This example tells InternetWriteFileFB to
82
 * return its third parameter:
83
 *   template<> template<>
84
 *   struct InternetWriteFileFB::Response::Info::ShouldMarshal<3> { static const bool value = true; };
85
 * By default, all parameters have ShouldMarshal set in the request phase
86
 * and only the return value (parameter -1) has it set in the response phase.
87
 *
88
 * * Marshalling special parameter/return types:
89
 *
90
 * The IPCTypeMap in FunctionBroker maps a parameter or return type
91
 * to a type that IpdlTuple knows how to marshal.  By default, the map is
92
 * the identity but some types need special handling.
93
 * The map is endpoint-specific (it is a member of the EndpointHandler),
94
 * so a different type can be used
95
 * for client -> server and for server -> client.  Note that the
96
 * types must be able to Copy() from one another -- the default Copy()
97
 * implementation uses the type's assignment operator.
98
 * The EndpointHandler itself is a template parameter of the FunctionBroker.
99
 * The default EndpointHandler recognizes basic types.
100
 * See e.g. FileDlgEndpointHandler<CLIENT>::IPCTypeMap<LPOPENFILENAMEW>
101
 * for an example of specialization.
102
 *
103
 * * Anything more complex involving parameter transmission:
104
 *
105
 * Sometimes marshaling parameters can require something more complex.  In
106
 * those cases, you will need to specialize the Marshal and Unmarshal
107
 * methods of the request or response handler and perform your complex logic
108
 * there.  A wise approach is to map your complex parameters into a simpler
109
 * parameter list and delegate the Marshal/Unmarshal calls to them.  For
110
 * example, an API might take a void* and an int as a buffer and length.
111
 * Obviously a void* cannot generally be marshaled.  However,  we can delegate
112
 * this call to a parameter list that takes a string in place of the buffer and
113
 * length.  Something like:
114
 *
115
 * typedef RequestHandler<ID_HookedFunc,
116
 *                        int HOOK_CALL (nsDependentCSubstring)> HookedFuncDelegateReq;
117
 *
118
 * template<>
119
 * void HookedFuncFB::Request::Marshal(IpdlTuple& aTuple, const void*& aBuf, const int& aBufLen)
120
 * {
121
 *   MOZ_ASSERT(nWritten);
122
 *   HookedFuncDelegateReq::Marshal(aTuple, nsDependentCSubstring(aBuf, aBufLen));
123
 * }
124
 *
125
 * template<>
126
 * bool HookedFuncFB::Request::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple,
127
 *                                       void*& aBuf, int& aBufLen)
128
 * {
129
 *   nsDependentCSubstring str;
130
 *   if (!HookedFuncDelegateReq::Unmarshal(aScd, aTuple, str)) {
131
 *     return false;
132
 *   }
133
 *
134
 *   // Request phase unmarshal uses ServerCallData for dynamically-allocating
135
 *   // memory.
136
 *   aScd.AllocateString(str, aBuf, false);
137
 *   aBufLen = str.Length();
138
 *   return true;
139
 * }
140
 *
141
 * See e.g. InternetWriteFileFB for a complete example of delegation.
142
 *
143
 * * Brokering but need the server to do more than just run the function:
144
 *
145
 * Specialize the FunctionBroker's RunFunction.  By default, it just runs
146
 * the function.  See GetSaveFileNameWFB for an example that does more.
147
 *
148
 */
149
150
namespace mozilla {
151
namespace plugins {
152
153
#if defined(XP_WIN)
154
155
// Currently, all methods we hook use the WINAPI calling convention.
156
#define HOOK_CALL WINAPI
157
158
typedef std::pair<ULONG_PTR, ULONG_PTR> UlongPair;
159
typedef std::map<UlongPair, uint64_t> UlongPairToIdMap;
160
extern UlongPairToIdMap sPairToIdMap;
161
typedef std::map<uint64_t, UlongPair> IdToUlongPairMap;
162
extern IdToUlongPairMap sIdToPairMap;
163
typedef std::map<void*, uint64_t> PtrToIdMap;
164
extern PtrToIdMap sPtrToIdMap;
165
typedef std::map<uint64_t, void*> IdToPtrMap;
166
extern IdToPtrMap sIdToPtrMap;
167
168
#else // defined(XP_WIN)
169
170
// Any methods we hook use the default calling convention.
171
#define HOOK_CALL
172
173
#endif // defined(XP_WIN)
174
175
0
inline bool IsOdd(uint64_t aVal) { return aVal & 1; }
176
177
// This enum is used to track if this process is currently running the client
178
// or server side of brokering.
179
enum Endpoint { SERVER, CLIENT };
180
0
inline const char *EndpointMsg(Endpoint aVal) { return aVal == SERVER ? "SERVER" : "CLIENT";  }
181
182
template <typename ParamType>
183
inline void LogParameterValue(int aIndex, const ParamType& aParam)
184
{
185
  // To avoid overhead, don't do this in release.
186
#ifdef DEBUG
187
  if (!MOZ_LOG_TEST(sPluginHooksLog, LogLevel::Verbose)) {
188
    return;
189
  }
190
  std::wstring paramString;
191
  IPC::LogParam(aParam, &paramString);
192
  HOOK_LOG(LogLevel::Verbose,
193
           ("Parameter %d: %S", aIndex, paramString.c_str()));
194
#endif
195
}
196
197
// This specialization is needed to log the common pattern where null is used
198
// as a fixed value for a pointer-type that is unknown to IPC.
199
template <typename ParamType>
200
inline void LogParameterValue(int aIndex, ParamType* const& aParam)
201
{
202
#ifdef DEBUG
203
  HOOK_LOG(LogLevel::Verbose,
204
           ("Parameter %d: pointer value - %p", aIndex, aParam));
205
#endif
206
}
207
208
template <>
209
inline void LogParameterValue(int aIndex, const nsDependentCSubstring& aParam)
210
0
{
211
0
#ifdef DEBUG
212
0
  HOOK_LOG(LogLevel::Verbose,
213
0
           ("Parameter %d : %s", aIndex, FormatBlob(aParam).Data()));
214
0
#endif
215
0
}
216
217
template <>
218
inline void LogParameterValue(int aIndex, char* const& aParam)
219
0
{
220
0
#ifdef DEBUG
221
0
  // A char* can be a block of raw memory.
222
0
  nsDependentCSubstring str;
223
0
  if (aParam) {
224
0
    str.Rebind(const_cast<char*>(aParam), strnlen(aParam, MAX_BLOB_CHARS_TO_LOG));
225
0
  } else {
226
0
    str.SetIsVoid(true);
227
0
  }
228
0
  LogParameterValue(aIndex, str);
229
0
#endif
230
0
}
231
232
template <>
233
inline void LogParameterValue(int aIndex, const char* const& aParam)
234
0
{
235
0
#ifdef DEBUG
236
0
  LogParameterValue(aIndex, const_cast<char* const&>(aParam));
237
0
#endif
238
0
}
239
240
#if defined(XP_WIN)
241
template <>
242
inline void LogParameterValue(int aIndex, const SEC_GET_KEY_FN& aParam)
243
{
244
#ifdef DEBUG
245
  MOZ_ASSERT(aParam == nullptr);
246
  HOOK_LOG(LogLevel::Verbose, ("Parameter %d: null function.", aIndex));
247
#endif
248
}
249
250
template <>
251
inline void LogParameterValue(int aIndex, LPVOID* const & aParam)
252
{
253
#ifdef DEBUG
254
  MOZ_ASSERT(aParam == nullptr);
255
  HOOK_LOG(LogLevel::Verbose, ("Parameter %d: null void pointer.", aIndex));
256
#endif
257
}
258
#endif // defined(XP_WIN)
259
260
// Used to check if a fixed parameter value is equal to the parameter given
261
// in the original function call.
262
template <typename ParamType>
263
inline bool ParameterEquality(const ParamType& aParam1, const ParamType& aParam2)
264
{
265
  return aParam1 == aParam2;
266
}
267
268
// Specialization: char* equality is string equality
269
template <>
270
inline bool ParameterEquality(char* const& aParam1, char* const& aParam2)
271
0
{
272
0
  return ((!aParam1 && !aParam2) ||
273
0
          (aParam1 && aParam2 && !strcmp(aParam1, aParam2)));
274
0
}
275
276
// Specialization: const char* const equality is string equality
277
template <>
278
inline bool ParameterEquality(const char* const& aParam1, const char* const& aParam2)
279
0
{
280
0
  return ParameterEquality(const_cast<char* const&>(aParam1), const_cast<char* const&>(aParam2));
281
0
}
282
283
/**
284
  * A type map _from_ the type of a parameter in the original function
285
  * we are brokering _to_ a type that we can marshal.  We must be able
286
  * to Copy() the marshaled type using the parameter type.
287
  * The default maps from type T back to type T.
288
  */
289
template<typename OrigType> struct IPCTypeMap     { typedef OrigType ipc_type; };
290
template<> struct IPCTypeMap<char*>               { typedef nsDependentCSubstring ipc_type; };
291
template<> struct IPCTypeMap<const char*>         { typedef nsDependentCSubstring ipc_type; };
292
template<> struct IPCTypeMap<long>                { typedef int32_t ipc_type; };
293
template<> struct IPCTypeMap<unsigned long>       { typedef uint32_t ipc_type; };
294
295
#if defined(XP_WIN)
296
template<> struct IPCTypeMap<PSecHandle>          { typedef uint64_t ipc_type; };
297
template<> struct IPCTypeMap<PTimeStamp>          { typedef uint64_t ipc_type; };
298
template<> struct IPCTypeMap<void*>               { typedef uint64_t ipc_type; }; // HANDLEs
299
template<> struct IPCTypeMap<HWND>                { typedef NativeWindowHandle ipc_type; };
300
template<> struct IPCTypeMap<PSCHANNEL_CRED>      { typedef IPCSchannelCred ipc_type; };
301
template<> struct IPCTypeMap<LPINTERNET_BUFFERSA> { typedef IPCInternetBuffers ipc_type; };
302
template<> struct IPCTypeMap<LPDWORD>             { typedef uint32_t ipc_type; };
303
#endif
304
305
template <typename AllocType>
306
static void DeleteDestructor(void* aObj) { delete static_cast<AllocType*>(aObj); }
307
308
extern void FreeDestructor(void* aObj);
309
310
// The ServerCallData is a list of ServerCallItems that should be freed when
311
// the server has completed a function call and marshaled a response.
312
class ServerCallData
313
{
314
public:
315
  typedef void (DestructorType)(void*);
316
317
  // Allocate a certain type.
318
  template <typename AllocType>
319
  AllocType* Allocate(DestructorType* aDestructor = &DeleteDestructor<AllocType>)
320
  {
321
    AllocType* ret = new AllocType();
322
    mList.AppendElement(FreeItem(ret, aDestructor));
323
    return ret;
324
  }
325
326
  template <typename AllocType>
327
  AllocType* Allocate(const AllocType& aValueToCopy,
328
                      DestructorType* aDestructor = &DeleteDestructor<AllocType>)
329
  {
330
    AllocType* ret = Allocate<AllocType>(aDestructor);
331
    *ret = aValueToCopy;
332
    return ret;
333
  }
334
335
  // Allocate memory, storing the pointer in buf.
336
  template <typename PtrType>
337
  void AllocateMemory(unsigned long aBufLen, PtrType& aBuf)
338
  {
339
    if (aBufLen) {
340
      aBuf = static_cast<PtrType>(malloc(aBufLen));
341
      mList.AppendElement(FreeItem(aBuf, FreeDestructor));
342
    } else {
343
      aBuf = nullptr;
344
    }
345
  }
346
347
  template <typename PtrType>
348
  void AllocateString(const nsACString& aStr, PtrType& aBuf, bool aCopyNullTerminator=true)
349
  {
350
    uint32_t nullByte = aCopyNullTerminator ? 1 : 0;
351
    char* tempBuf = static_cast<char*>(malloc(aStr.Length() + nullByte));
352
    memcpy(tempBuf, aStr.Data(), aStr.Length() + nullByte);
353
    mList.AppendElement(FreeItem(tempBuf, FreeDestructor));
354
    aBuf = tempBuf;
355
  }
356
357
  // Run the given destructor on the given memory, for special cases where
358
  // memory is allocated elsewhere but must still be freed.
359
  void PostDestructor(void* aMem, DestructorType* aDestructor)
360
0
  {
361
0
    mList.AppendElement(FreeItem(aMem, aDestructor));
362
0
  }
363
364
#if defined(XP_WIN)
365
  // Allocate memory and a DWORD block-length, storing them in the
366
  // corresponding parameters.
367
  template <typename PtrType>
368
  void AllocateMemory(DWORD aBufLen, PtrType& aBuf, LPDWORD& aBufLenCopy)
369
  {
370
    aBufLenCopy = static_cast<LPDWORD>(malloc(sizeof(DWORD)));
371
    *aBufLenCopy = aBufLen;
372
    mList.AppendElement(FreeItem(aBufLenCopy, FreeDestructor));
373
    AllocateMemory(aBufLen, aBuf);
374
  }
375
#endif // defined(XP_WIN)
376
377
private:
378
  // FreeItems are used to free objects that were temporarily needed for
379
  // dispatch, such as buffers that are given as a parameter.
380
  class FreeItem
381
  {
382
    void* mPtr;
383
    DestructorType* mDestructor;
384
    FreeItem(FreeItem& aOther);  // revoked
385
  public:
386
    explicit FreeItem(void* aPtr, DestructorType* aDestructor) :
387
        mPtr(aPtr)
388
      , mDestructor(aDestructor)
389
0
    {
390
0
      MOZ_ASSERT(mDestructor || !aPtr);
391
0
    }
392
393
    FreeItem(FreeItem&& aOther) :
394
        mPtr(aOther.mPtr)
395
      , mDestructor(aOther.mDestructor)
396
0
    {
397
0
      aOther.mPtr = nullptr;
398
0
      aOther.mDestructor = nullptr;
399
0
    }
400
401
0
    ~FreeItem() { if (mDestructor) { mDestructor(mPtr); } }
402
  };
403
404
  typedef nsTArray<FreeItem> FreeItemList;
405
  FreeItemList mList;
406
};
407
408
// Holds an IpdlTuple and a ServerCallData.  This is used by the phase handlers
409
// (RequestHandler and ResponseHandler) in the Unmarshaling phase.
410
// Server-side unmarshaling (during the request phase) uses a ServerCallData
411
// to keep track of allocated memory.  In the client, ServerCallDatas are
412
// not used and that value will always be null.
413
class IpdlTupleContext
414
{
415
public:
416
  explicit IpdlTupleContext(const IpdlTuple* aTuple, ServerCallData* aScd=nullptr) :
417
    mTuple(aTuple), mScd(aScd)
418
0
  {
419
0
    MOZ_ASSERT(aTuple);
420
0
  }
421
422
0
  ServerCallData* GetServerCallData() { return mScd; }
423
0
  const IpdlTuple* GetIpdlTuple() { return mTuple;  }
424
425
private:
426
  const IpdlTuple* mTuple;
427
  ServerCallData* mScd;
428
};
429
430
template<typename DestType, typename SrcType>
431
inline void Copy(DestType& aDest, const SrcType& aSrc)
432
{
433
  aDest = (DestType)aSrc;
434
}
435
436
template<>
437
inline void Copy(nsDependentCSubstring& aDest, const nsDependentCSubstring& aSrc)
438
0
{
439
0
  if (aSrc.IsVoid()) {
440
0
    aDest.SetIsVoid(true);
441
0
  } else {
442
0
    aDest.Rebind(aSrc.Data(), aSrc.Length());
443
0
  }
444
0
}
445
446
#if defined(XP_WIN)
447
448
template<>
449
inline void Copy(uint64_t& aDest, const PTimeStamp& aSrc)
450
{
451
  aDest = static_cast<uint64_t>(aSrc->QuadPart);
452
}
453
454
template<>
455
inline void Copy(PTimeStamp& aDest, const uint64_t& aSrc)
456
{
457
  aDest->QuadPart = static_cast<LONGLONG>(aSrc);
458
}
459
460
#endif // defined(XP_WIN)
461
462
template<Endpoint e, typename SelfType> struct BaseEndpointHandler;
463
template<typename SelfType>
464
struct BaseEndpointHandler<CLIENT,SelfType> {
465
  static const Endpoint OtherSide = SERVER;
466
467
  template<typename DestType, typename SrcType>
468
  inline static void Copy(ServerCallData* aScd, DestType& aDest, const SrcType& aSrc)
469
  {
470
    MOZ_ASSERT(!aScd);    // never used in the CLIENT
471
    SelfType::Copy(aDest, aSrc);
472
  }
473
474
  template<typename DestType, typename SrcType>
475
  inline static void Copy(DestType& aDest, const SrcType& aSrc)
476
  {
477
    mozilla::plugins::Copy(aDest, aSrc);
478
  }
479
480
  // const char* should be null terminated but this is not always the case.
481
  // In those cases, we must override this default behavior.
482
  inline static void Copy(nsDependentCSubstring& aDest, const char* const& aSrc)
483
  {
484
    // In the client, we just bind to the caller's string
485
    if (aSrc) {
486
      aDest.Rebind(aSrc, strlen(aSrc));
487
    } else {
488
      aDest.SetIsVoid(true);
489
    }
490
  }
491
492
  inline static void Copy(const char*& aDest, const nsDependentCSubstring& aSrc)
493
  {
494
    MOZ_ASSERT_UNREACHABLE("Cannot return const parameters.");
495
  }
496
497
  inline static void Copy(nsDependentCSubstring& aDest, char* const& aSrc)
498
  {
499
    // In the client, we just bind to the caller's string
500
    if (aSrc) {
501
      aDest.Rebind(aSrc, strlen(aSrc));
502
    } else {
503
      aDest.SetIsVoid(true);
504
    }
505
  }
506
507
  inline static void Copy(char*& aDest, const nsDependentCSubstring& aSrc)
508
  {
509
    MOZ_ASSERT_UNREACHABLE("Returning char* parameters is not yet suported.");
510
  }
511
512
#if defined(XP_WIN)
513
  inline static void Copy(uint32_t& aDest, const LPDWORD& aSrc)
514
  {
515
    aDest = *aSrc;
516
  }
517
518
  inline static void Copy(LPDWORD& aDest, const uint32_t& aSrc)
519
  {
520
    *aDest = aSrc;
521
  }
522
#endif // #if defined(XP_WIN)
523
};
524
525
template<typename SelfType>
526
struct BaseEndpointHandler<SERVER, SelfType> {
527
  static const Endpoint OtherSide = CLIENT;
528
529
  // Specializations of this method may allocate memory for types that need it
530
  // during Unmarshaling.  They record the allocation in the ServerCallData.
531
  // When copying values in the SERVER, we should be sure to carefully validate
532
  // the information that came from the client as the client may be compromised
533
  // by malicious code.
534
  template<typename DestType, typename SrcType>
535
  inline static void Copy(ServerCallData* aScd, DestType& aDest, const SrcType& aSrc)
536
  {
537
    SelfType::Copy(aDest, aSrc);
538
  }
539
540
  template<typename DestType, typename SrcType>
541
  inline static void Copy(DestType& aDest, const SrcType& aSrc)
542
  {
543
    mozilla::plugins::Copy(aDest, aSrc);
544
  }
545
546
  inline static void Copy(nsDependentCSubstring& aDest, const nsDependentCSubstring& aSrc)
547
  {
548
    aDest.Rebind(aSrc.Data(), aSrc.Length());
549
    aDest.SetIsVoid(aSrc.IsVoid());
550
  }
551
552
  // const char* should be null terminated but this is not always the case.
553
  // In those cases, we override this default behavior.
554
  inline static void Copy(nsDependentCSubstring& aDest, const char* const& aSrc)
555
  {
556
    MOZ_ASSERT_UNREACHABLE("Const parameter cannot be returned by brokering process.");
557
  }
558
559
  inline static void Copy(nsDependentCSubstring& aDest, char* const& aSrc)
560
  {
561
    MOZ_ASSERT_UNREACHABLE("Returning char* parameters is not yet suported.");
562
  }
563
564
  inline static void Copy(ServerCallData* aScd, char*& aDest, const nsDependentCSubstring& aSrc)
565
  {
566
    // In the parent, we must allocate the string.
567
    MOZ_ASSERT(aScd);
568
    if (aSrc.IsVoid()) {
569
      aDest = nullptr;
570
      return;
571
    }
572
    aScd->AllocateMemory(aSrc.Length() + 1, aDest);
573
    memcpy(aDest, aSrc.Data(), aSrc.Length());
574
    aDest[aSrc.Length()] = '\0';
575
  }
576
577
  inline static void Copy(ServerCallData* aScd, const char*& aDest, const nsDependentCSubstring& aSrc)
578
  {
579
    char* nonConstDest;
580
    Copy(aScd, nonConstDest, aSrc);
581
    aDest = nonConstDest;
582
  }
583
584
#if defined(XP_WIN)
585
  inline static void Copy(uint32_t& aDest, const LPDWORD& aSrc)
586
  {
587
    aDest = *aSrc;
588
  }
589
590
  inline static void Copy(LPDWORD& aDest, const uint32_t& aSrc)
591
  {
592
    MOZ_RELEASE_ASSERT(aDest);
593
    *aDest = aSrc;
594
  }
595
596
  inline static void Copy(ServerCallData* aScd, PTimeStamp& aDest, const uint64_t& aSrc)
597
  {
598
    MOZ_ASSERT(!aDest);
599
    aDest = aScd->Allocate<::TimeStamp>();
600
    Copy(aDest, aSrc);
601
  }
602
#endif // defined(XP_WIN)
603
};
604
605
// PhaseHandler is a RequestHandler or a ResponseHandler.
606
template<Endpoint endpoint, typename PhaseHandler>
607
struct Marshaler
608
{
609
  // Driver
610
  template<int firstIndex = 0, typename ... VarParams>
611
  static void Marshal(IpdlTuple& aMarshaledTuple,
612
                         const VarParams&... aParams)
613
  {
614
    MarshalParameters<firstIndex>(aMarshaledTuple, aParams...);
615
  }
616
617
  // Driver
618
  template<int firstIndex = 0, typename ... VarParams>
619
  static bool Unmarshal(IpdlTupleContext& aUnmarshaledTuple, VarParams&... aParams)
620
  {
621
    return UnmarshalParameters<firstIndex>(aUnmarshaledTuple, 0, aParams...);
622
  }
623
624
  template<int paramIndex, typename OrigType,
625
           bool shouldMarshal = PhaseHandler::Info::template ShouldMarshal<paramIndex>::value>
626
  struct MaybeMarshalParameter {};
627
628
  /**
629
   * shouldMarshal = true case
630
   */
631
  template<int paramIndex, typename OrigType>
632
  struct MaybeMarshalParameter<paramIndex, OrigType, true>
633
  {
634
    template<typename IPCType = typename PhaseHandler::template IPCTypeMap<OrigType>::ipc_type>
635
    static void MarshalParameter(IpdlTuple& aMarshaledTuple, const OrigType& aParam)
636
    {
637
      HOOK_LOG(LogLevel::Verbose,
638
               ("%s marshaling parameter %d.", EndpointMsg(endpoint), paramIndex));
639
      IPCType ipcObject;
640
      // EndpointHandler must be able to Copy() from OrigType to IPCType
641
      PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(ipcObject, aParam);
642
      LogParameterValue(paramIndex, ipcObject);
643
      aMarshaledTuple.AddElement(ipcObject);
644
    }
645
  };
646
647
  /**
648
   * shouldMarshal = false case
649
   */
650
  template<int paramIndex, typename OrigType>
651
  struct MaybeMarshalParameter<paramIndex, OrigType, false>
652
  {
653
    static void MarshalParameter(IpdlTuple& aMarshaledTuple, const OrigType& aParam)
654
    {
655
      HOOK_LOG(LogLevel::Verbose,
656
               ("%s not marshaling parameter %d.", EndpointMsg(endpoint), paramIndex));
657
    }
658
  };
659
660
  /**
661
   * Recursive case: marshals aFirstParam to aMarshaledTuple (if desired),
662
   * then marshals the aRemainingParams.
663
   */
664
  template<int paramIndex,
665
           typename VarParam,
666
           typename ... VarParams>
667
  static void MarshalParameters(IpdlTuple& aMarshaledTuple,
668
                         const VarParam& aFirstParam,
669
                         const VarParams&... aRemainingParams)
670
  {
671
    MaybeMarshalParameter<paramIndex, VarParam>::MarshalParameter(aMarshaledTuple, aFirstParam);
672
    MarshalParameters<paramIndex + 1, VarParams...>(aMarshaledTuple,
673
                                              aRemainingParams...);
674
  }
675
676
  /**
677
   * Base case: empty parameter list -- nothing to marshal.
678
   */
679
  template <int paramIndex> static void MarshalParameters(IpdlTuple& aMarshaledTuple) {}
680
681
  template<int tupleIndex, typename OrigType,
682
           bool shouldMarshal = PhaseHandler::Info::template ShouldMarshal<tupleIndex>::value,
683
           bool hasFixedValue = PhaseHandler::Info::template HasFixedValue<tupleIndex>::value>
684
  struct MaybeUnmarshalParameter {};
685
686
  /**
687
   * ShouldMarshal = true case.  HasFixedValue must be false in that case.
688
   */
689
  template<int tupleIndex, typename VarParam>
690
  struct MaybeUnmarshalParameter<tupleIndex, VarParam, true, false>
691
  {
692
    template<typename IPCType = typename PhaseHandler::template IPCTypeMap<VarParam>::ipc_type>
693
    static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple, int& aNextTupleIdx, VarParam& aParam)
694
    {
695
      const IPCType* ipcObject = aUnmarshaledTuple.GetIpdlTuple()->Element<IPCType>(aNextTupleIdx);
696
      if (!ipcObject) {
697
        HOOK_LOG(LogLevel::Error,
698
                 ("%s failed to unmarshal parameter %d.", EndpointMsg(endpoint), tupleIndex));
699
        return false;
700
      }
701
      HOOK_LOG(LogLevel::Verbose,
702
               ("%s unmarshaled parameter %d.", EndpointMsg(endpoint), tupleIndex));
703
      LogParameterValue(tupleIndex, *ipcObject);
704
      PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(aUnmarshaledTuple.GetServerCallData(), aParam, *ipcObject);
705
      ++aNextTupleIdx;
706
      return true;
707
    }
708
  };
709
710
  /**
711
   * ShouldMarshal = true : nsDependentCSubstring specialization
712
   */
713
  template<int tupleIndex>
714
  struct MaybeUnmarshalParameter<tupleIndex, nsDependentCSubstring, true, false>
715
  {
716
    static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple, int& aNextTupleIdx, nsDependentCSubstring& aParam)
717
    {
718
      // Deserialize as an nsCString and then copy the info into the nsDependentCSubstring
719
      const nsCString* ipcObject = aUnmarshaledTuple.GetIpdlTuple()->Element<nsCString>(aNextTupleIdx);
720
      if (!ipcObject) {
721
        HOOK_LOG(LogLevel::Error,
722
                 ("%s failed to unmarshal parameter %d.", EndpointMsg(endpoint), tupleIndex));
723
        return false;
724
      }
725
      HOOK_LOG(LogLevel::Verbose,
726
               ("%s unmarshaled parameter %d.", EndpointMsg(endpoint), tupleIndex));
727
728
      aParam.Rebind(ipcObject->Data(), ipcObject->Length());
729
      aParam.SetIsVoid(ipcObject->IsVoid());
730
      LogParameterValue(tupleIndex, aParam);
731
      ++aNextTupleIdx;
732
      return true;
733
    }
734
  };
735
736
  /**
737
   * ShouldMarshal = true : char* specialization
738
   */
739
  template<int tupleIndex>
740
  struct MaybeUnmarshalParameter<tupleIndex, char*, true, false>
741
  {
742
    static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple, int& aNextTupleIdx, char*& aParam)
743
    {
744
      nsDependentCSubstring tempStr;
745
      bool ret = MaybeUnmarshalParameter<tupleIndex, nsDependentCSubstring, true, false>::UnmarshalParameter(aUnmarshaledTuple, aNextTupleIdx, tempStr);
746
      PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(aUnmarshaledTuple.GetServerCallData(), aParam, tempStr);
747
      return ret;
748
    }
749
  };
750
751
  /**
752
   * ShouldMarshal = true : const char* specialization
753
   */
754
  template<int tupleIndex>
755
  struct MaybeUnmarshalParameter<tupleIndex, const char*, true, false>
756
  {
757
    static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple, int& aNextTupleIdx, const char*& aParam)
758
    {
759
      char* tempStr;
760
      bool ret = MaybeUnmarshalParameter<tupleIndex, char*, true, false>::UnmarshalParameter(aUnmarshaledTuple, aNextTupleIdx, tempStr);
761
      aParam = tempStr;
762
      return ret;
763
    }
764
  };
765
766
  /**
767
   * ShouldMarshal = false, fixed parameter case
768
   */
769
  template<int tupleIndex, typename VarParam>
770
  struct MaybeUnmarshalParameter<tupleIndex, VarParam, false, true>
771
  {
772
    static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,  int& aNextTupleIdx, VarParam& aParam) {
773
      // Copy default value if this is client->server communication (and if it exists)
774
      PhaseHandler::template CopyFixedParam<tupleIndex, VarParam>(aParam);
775
      HOOK_LOG(LogLevel::Verbose,
776
               ("%s parameter %d not unmarshaling -- using fixed value.", EndpointMsg(endpoint), tupleIndex));
777
      LogParameterValue(tupleIndex, aParam);
778
      return true;
779
    }
780
  };
781
782
  /**
783
   * ShouldMarshal = false, unfixed parameter case.  Assume user has done special handling.
784
   */
785
  template<int tupleIndex, typename VarParam>
786
  struct MaybeUnmarshalParameter<tupleIndex, VarParam, false, false>
787
  {
788
    static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,  int& aNextTupleIdx, VarParam& aParam) {
789
      HOOK_LOG(LogLevel::Verbose,
790
               ("%s parameter %d not automatically unmarshaling.", EndpointMsg(endpoint), tupleIndex));
791
      // DLP: TODO: specializations fail LogParameterValue(tupleIndex, aParam);
792
      return true;
793
    }
794
  };
795
796
  /**
797
   * Recursive case: unmarshals aFirstParam to aUnmarshaledTuple (if desired),
798
   * then unmarshals the aRemainingParams.
799
   * The endpoint specifies the side this process is on: client or server.
800
   */
801
  template <int tupleIndex,
802
            typename VarParam,
803
            typename ... VarParams>
804
  static bool UnmarshalParameters(IpdlTupleContext& aUnmarshaledTuple, int aNextTupleIdx,
805
                           VarParam& aFirstParam, VarParams&... aRemainingParams)
806
  {
807
    // TODO: DLP: I currently increment aNextTupleIdx in the method (its a reference).  This is awful.
808
    if (!MaybeUnmarshalParameter<tupleIndex, VarParam>::UnmarshalParameter(aUnmarshaledTuple, aNextTupleIdx, aFirstParam)) {
809
      return false;
810
    }
811
    return UnmarshalParameters<tupleIndex + 1, VarParams...>(aUnmarshaledTuple, aNextTupleIdx, aRemainingParams...);
812
  }
813
814
  /**
815
   * Base case: empty parameter list -- nothing to unmarshal.
816
   */
817
  template <int>
818
  static bool UnmarshalParameters(IpdlTupleContext& aUnmarshaledTuple, int aNextTupleIdx)
819
  {
820
    return true;
821
  }
822
};
823
824
825
// The default marshals all parameters.
826
template<FunctionHookId functionId> struct RequestInfo
827
{
828
  template<int paramIndex> struct FixedValue;
829
830
  template<int paramIndex, typename = int> struct HasFixedValue { static const bool value = false; };
831
  template<int paramIndex> struct HasFixedValue<paramIndex, decltype(FixedValue<paramIndex>::value,0)>
832
  {
833
    static const bool value = true;
834
  };
835
836
  // By default we the request should marshal any non-fixed parameters.
837
  template<int paramIndex>
838
  struct ShouldMarshal { static const bool value = !HasFixedValue<paramIndex>::value; };
839
};
840
841
/**
842
 * This base stores the RequestHandler's IPCTypeMap.  It really only
843
 * exists to circumvent the arbitrary C++ rule (enforced by mingw) forbidding
844
 * full class specialization of a class (IPCTypeMap<T>) inside of an
845
 * unspecialized template class (RequestHandler<T>).
846
 */
847
struct RequestHandlerBase
848
{
849
  // Default to the namespace-level IPCTypeMap
850
  template<typename OrigType>
851
  struct IPCTypeMap
852
  {
853
    typedef typename mozilla::plugins::IPCTypeMap<OrigType>::ipc_type ipc_type;
854
  };
855
};
856
857
#if defined(XP_WIN)
858
859
// Request phase uses OpenFileNameIPC for an LPOPENFILENAMEW parameter.
860
template<>
861
struct RequestHandlerBase::IPCTypeMap<LPOPENFILENAMEW> { typedef OpenFileNameIPC ipc_type; };
862
863
#endif // defined(XP_WIN)
864
865
struct BaseEHContainer {
866
  template <Endpoint e> struct EndpointHandler : public BaseEndpointHandler<e,EndpointHandler<e>> {};
867
};
868
869
template<FunctionHookId functionId, typename FunctionType, typename EHContainer> struct RequestHandler;
870
871
template<FunctionHookId functionId, typename EHContainerType, typename ResultType, typename ... ParamTypes>
872
struct RequestHandler<functionId, ResultType HOOK_CALL (ParamTypes...), EHContainerType> :
873
  public RequestHandlerBase
874
{
875
  typedef ResultType(HOOK_CALL FunctionType)(ParamTypes...);
876
  typedef RequestHandler<functionId, FunctionType, EHContainerType> SelfType;
877
  typedef RequestInfo<functionId> Info;
878
  typedef EHContainerType EHContainer;
879
880
  static void Marshal(IpdlTuple& aTuple, const ParamTypes&... aParams)
881
  {
882
    ReqMarshaler::Marshal(aTuple, aParams...);
883
  }
884
885
  static bool Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple, ParamTypes&... aParams)
886
  {
887
    IpdlTupleContext cxt(&aTuple, &aScd);
888
    return ReqUnmarshaler::Unmarshal(cxt, aParams...);
889
  }
890
891
  typedef Marshaler<CLIENT, SelfType> ReqMarshaler;
892
  typedef Marshaler<SERVER, SelfType> ReqUnmarshaler;
893
894
  /**
895
   * Returns true if a call made with the given parameters should be
896
   * brokered (vs. passed-through to the original function).
897
   */
898
  static bool ShouldBroker(Endpoint aEndpoint, const ParamTypes&... aParams)
899
  {
900
    // True if all filtered parameters match their filter value.
901
    return CheckFixedParams(aParams...);
902
  }
903
904
  template <int paramIndex, typename VarParam>
905
  static void CopyFixedParam(VarParam& aParam)
906
  {
907
    aParam = Info::template FixedValue<paramIndex>::value;
908
  }
909
910
protected:
911
  // Returns true if filtered parameters match their filter value.
912
  static bool CheckFixedParams(const ParamTypes&... aParams)
913
  {
914
    return CheckFixedParamsHelper<0>(aParams...);
915
  }
916
917
  // If no FixedValue<paramIndex> is defined and equal to FixedType then always pass.
918
  template<int paramIndex, typename = int>
919
  struct CheckFixedParam
920
  {
921
    template<typename ParamType>
922
    static inline bool Check(const ParamType& aParam) { return true; }
923
  };
924
925
  // If FixedValue<paramIndex> is defined then check equality.
926
  template<int paramIndex>
927
  struct CheckFixedParam<paramIndex, decltype(Info::template FixedValue<paramIndex>::value,0)>
928
  {
929
    template<typename ParamType>
930
    static inline bool Check(ParamType& aParam)
931
    {
932
      return ParameterEquality(aParam, Info::template FixedValue<paramIndex>::value);
933
    }
934
  };
935
936
  // Recursive case: Chcek head parameter, then tail parameters.
937
  template<int index, typename VarParam, typename ... VarParams>
938
  static bool CheckFixedParamsHelper(const VarParam& aParam, const VarParams&... aParams)
939
  {
940
    if (!CheckFixedParam<index>::Check(aParam)) {
941
      return false;     // didn't match a fixed parameter
942
    }
943
    return CheckFixedParamsHelper<index + 1>(aParams...);
944
  }
945
946
  // Base case: All fixed parameters matched.
947
  template<int> static bool CheckFixedParamsHelper() { return true; }
948
};
949
950
// The default returns no parameters -- only the return value.
951
template<FunctionHookId functionId>
952
struct ResponseInfo
953
{
954
  template<int paramIndex> struct HasFixedValue
955
  {
956
    static const bool value = RequestInfo<functionId>::template HasFixedValue<paramIndex>::value;
957
  };
958
959
  // Only the return value (index -1) is sent by default.
960
  template<int paramIndex> struct ShouldMarshal { static const bool value = (paramIndex == -1); };
961
962
  // This is the condition on the function result that we use to determine if
963
  // the windows thread-local error state should be sent to the client.  The
964
  // error is typically only relevant if the function did not succeed.
965
  template<typename ResultType>
966
  static bool ShouldTransmitError(const ResultType& aResult) {
967
    return !static_cast<bool>(aResult);
968
  }
969
};
970
971
/**
972
 * Same rationale as for RequestHandlerBase.
973
 */
974
struct ResponseHandlerBase
975
{
976
  // Default to the namespace-level IPCTypeMap
977
  template<typename OrigType>
978
  struct IPCTypeMap
979
  {
980
    typedef typename mozilla::plugins::IPCTypeMap<OrigType>::ipc_type ipc_type;
981
  };
982
};
983
984
#if defined(XP_WIN)
985
986
// Response phase uses OpenFileNameRetIPC for an LPOPENFILENAMEW parameter.
987
template<>
988
struct ResponseHandlerBase::IPCTypeMap<LPOPENFILENAMEW> { typedef OpenFileNameRetIPC ipc_type; };
989
990
#endif
991
992
template<FunctionHookId functionId, typename FunctionType, typename EHContainer> struct ResponseHandler;
993
994
template<FunctionHookId functionId, typename EHContainerType, typename ResultType, typename ... ParamTypes>
995
struct ResponseHandler<functionId, ResultType HOOK_CALL (ParamTypes...), EHContainerType> :
996
  public ResponseHandlerBase
997
{
998
  typedef ResultType(HOOK_CALL FunctionType)(ParamTypes...);
999
  typedef ResponseHandler<functionId, FunctionType, EHContainerType> SelfType;
1000
  typedef ResponseInfo<functionId> Info;
1001
  typedef EHContainerType EHContainer;
1002
1003
  static void Marshal(IpdlTuple& aTuple, const ResultType& aResult, const ParamTypes&... aParams)
1004
  {
1005
    // Note that this "trick" means that the first parameter we marshal is
1006
    // considered to be parameter #-1 when checking the ResponseInfo.
1007
    // The parameters in the list therefore start at index 0.
1008
    RspMarshaler::template Marshal<-1>(aTuple, aResult, aParams...);
1009
  }
1010
  static bool Unmarshal(const IpdlTuple& aTuple, ResultType& aResult, ParamTypes&... aParams)
1011
  {
1012
    IpdlTupleContext cxt(&aTuple);
1013
    return RspUnmarshaler::template Unmarshal<-1>(cxt, aResult, aParams...);
1014
  }
1015
1016
  typedef Marshaler<SERVER, SelfType> RspMarshaler;
1017
  typedef Marshaler<CLIENT, SelfType> RspUnmarshaler;
1018
1019
  // Fixed parameters are not used in the response phase.
1020
  template <int tupleIndex, typename VarParam>
1021
  static void CopyFixedParam(VarParam& aParam) {}
1022
};
1023
1024
/**
1025
 * Reference-counted monitor, used to synchronize communication between a
1026
 * thread using a brokered API and the FunctionDispatch thread.
1027
 */
1028
class FDMonitor : public Monitor
1029
{
1030
public:
1031
  FDMonitor() : Monitor("FunctionDispatchThread lock")
1032
0
  {}
1033
1034
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FDMonitor)
1035
1036
private:
1037
0
  ~FDMonitor() {}
1038
};
1039
1040
/**
1041
 * Data for hooking a function that we automatically broker in a remote
1042
 * process.
1043
 */
1044
template <FunctionHookId functionId, typename FunctionType,
1045
          typename EHContainer = BaseEHContainer>
1046
class FunctionBroker;
1047
1048
template <FunctionHookId functionId, typename EHContainer, typename ResultType, typename ... ParamTypes>
1049
class FunctionBroker<functionId, ResultType HOOK_CALL (ParamTypes...), EHContainer> :
1050
  public BasicFunctionHook<functionId, ResultType HOOK_CALL (ParamTypes...)>
1051
{
1052
public:
1053
  typedef Tuple<ParamTypes...> TupleParamTypes;
1054
  typedef Tuple<mozilla::Maybe<ParamTypes>...> TupleMaybeParamTypes;
1055
  typedef Tuple<ParamTypes*...> TupleParamPtrTypes;
1056
  typedef Tuple<ParamTypes&...> TupleParamRefTypes;
1057
  static const size_t numParams = sizeof...(ParamTypes);
1058
1059
  typedef ResultType (HOOK_CALL FunctionType)(ParamTypes...);
1060
  typedef FunctionBroker<functionId, FunctionType, EHContainer> SelfType;
1061
  typedef BasicFunctionHook<functionId, FunctionType> FunctionHookInfoType;
1062
  typedef FunctionHookInfoType BaseType;
1063
1064
  typedef RequestHandler<functionId, FunctionType, EHContainer> Request;
1065
  typedef ResponseHandler<functionId, FunctionType, EHContainer> Response;
1066
1067
  template <typename DelegateFcnType>
1068
  using RequestDelegate = RequestHandler<functionId, DelegateFcnType, EHContainer>;
1069
  template <typename DelegateFcnType>
1070
  using ResponseDelegate = ResponseHandler<functionId, DelegateFcnType, EHContainer>;
1071
1072
  FunctionBroker(const char* aModuleName, const char* aMethodName,
1073
                       FunctionType* aOriginalFunction) :
1074
    BasicFunctionHook<functionId, FunctionType>(aModuleName, aMethodName,
1075
                                                aOriginalFunction, InterceptorStub)
1076
  {
1077
  }
1078
1079
  // This is the function used to replace the original DLL-intercepted function.
1080
  static ResultType HOOK_CALL InterceptorStub(ParamTypes... aParams)
1081
  {
1082
    MOZ_ASSERT(functionId < FunctionHook::GetHooks()->Length());
1083
    FunctionHook* self = FunctionHook::GetHooks()->ElementAt(functionId);
1084
    MOZ_ASSERT(self && self->FunctionId() == functionId);
1085
    const SelfType* broker = static_cast<const SelfType*>(self);
1086
    return broker->MaybeBrokerCallClient(aParams...);
1087
  }
1088
1089
  /**
1090
   * Handle a call by running the original version or brokering, depending on
1091
   * ShouldBroker.  All parameter types (including the result type)
1092
   * must have IPDL ParamTraits specializations or appear in this object's
1093
   * IPCTypeMap.  If brokering fails for any reason then this falls back to
1094
   * calling the original version of the function.
1095
   */
1096
  ResultType MaybeBrokerCallClient(ParamTypes&... aParameters) const;
1097
1098
  /**
1099
   * Called server-side to run the original function using aInTuple
1100
   * as parameter values.  The return value and returned parameters
1101
   * (in that order) are added to aOutTuple.
1102
   */
1103
  bool
1104
  RunOriginalFunction(base::ProcessId aClientId, const IPC::IpdlTuple &aInTuple,
1105
                      IPC::IpdlTuple *aOutTuple) const override
1106
  {
1107
    return BrokerCallServer(aClientId, aInTuple, aOutTuple);
1108
  }
1109
1110
protected:
1111
  bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple &aInTuple,
1112
                        IpdlTuple *aOutTuple) const
1113
  {
1114
    return BrokerCallServer(aClientId, aInTuple, aOutTuple,
1115
                             std::index_sequence_for<ParamTypes...>{});
1116
  }
1117
1118
  bool BrokerCallClient(uint32_t& aWinError, ResultType& aResult, ParamTypes&... aParameters) const;
1119
  bool PostToDispatchThread(uint32_t& aWinError, ResultType& aRet, ParamTypes&... aParameters) const;
1120
1121
  static void
1122
  PostToDispatchHelper(const SelfType* bmhi, RefPtr<FDMonitor> monitor, bool* notified,
1123
                       bool* ok, uint32_t* winErr, ResultType* r, ParamTypes*... p)
1124
  {
1125
    // Note: p is also non-null... its just hard to assert that.
1126
    MOZ_ASSERT(bmhi && monitor && notified && ok && winErr && r);
1127
    MOZ_ASSERT(*notified == false);
1128
    *ok = bmhi->BrokerCallClient(*winErr, *r, *p...);
1129
1130
    {
1131
      // We need to grab the lock to make sure that Wait() has been
1132
      // called in PostToDispatchThread.  We need that since we wake it with
1133
      // Notify().
1134
      MonitorAutoLock lock(*monitor);
1135
      *notified = true;
1136
    }
1137
1138
    monitor->Notify();
1139
  };
1140
1141
  template<typename ... VarParams>
1142
  ResultType
1143
  RunFunction(FunctionType* aFunction, base::ProcessId aClientId,
1144
                VarParams&... aParams) const
1145
  {
1146
    return aFunction(aParams...);
1147
  };
1148
1149
  bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple &aInTuple,
1150
                        IpdlTuple *aOutTuple, ParamTypes&... aParams) const;
1151
1152
  template<size_t... Indices>
1153
  bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple &aInTuple,
1154
                         IpdlTuple *aOutTuple, std::index_sequence<Indices...>) const
1155
  {
1156
    TupleParamTypes paramTuple;
1157
    return BrokerCallServer(aClientId, aInTuple, aOutTuple,
1158
                             Get<Indices>(paramTuple)...);
1159
  }
1160
};
1161
1162
template <FunctionHookId functionId, typename EHContainer, typename ResultType, typename ... ParamTypes>
1163
ResultType
1164
FunctionBroker<functionId, ResultType HOOK_CALL (ParamTypes...), EHContainer>::MaybeBrokerCallClient(ParamTypes&... aParameters) const
1165
{
1166
  MOZ_ASSERT(FunctionBrokerChild::GetInstance());
1167
1168
  // Broker the call if ShouldBroker says to.  Otherwise, or if brokering
1169
  // fails, then call the original implementation.
1170
  if (!FunctionBrokerChild::GetInstance()) {
1171
    HOOK_LOG(LogLevel::Error,
1172
             ("[%s] Client attempted to broker call without actor.", FunctionHookInfoType::mFunctionName.Data()));
1173
  }
1174
  else if (Request::ShouldBroker(CLIENT, aParameters...)) {
1175
    HOOK_LOG(LogLevel::Debug,
1176
             ("[%s] Client attempting to broker call.", FunctionHookInfoType::mFunctionName.Data()));
1177
    uint32_t winError;
1178
    ResultType ret;
1179
    bool success = BrokerCallClient(winError, ret, aParameters...);
1180
    HOOK_LOG(LogLevel::Info,
1181
             ("[%s] Client brokering %s.", FunctionHookInfoType::mFunctionName.Data(), SuccessMsg(success)));
1182
    if (success) {
1183
#if defined(XP_WIN)
1184
      if (Response::Info::ShouldTransmitError(ret)) {
1185
        HOOK_LOG(LogLevel::Debug,
1186
                 ("[%s] Client setting thread error code: %08x.", FunctionHookInfoType::mFunctionName.Data(), winError));
1187
        ::SetLastError(winError);
1188
      }
1189
#endif
1190
      return ret;
1191
    }
1192
  }
1193
1194
  HOOK_LOG(LogLevel::Info,
1195
            ("[%s] Client could not broker.  Running original version.", FunctionHookInfoType::mFunctionName.Data()));
1196
  return FunctionHookInfoType::mOldFunction(aParameters...);
1197
}
1198
1199
template <FunctionHookId functionId, typename EHContainer, typename ResultType, typename ... ParamTypes>
1200
bool
1201
FunctionBroker<functionId, ResultType HOOK_CALL (ParamTypes...), EHContainer>::BrokerCallClient(uint32_t& aWinError,
1202
                                                                  ResultType& aResult,
1203
                                                                  ParamTypes&... aParameters) const
1204
{
1205
  if (!FunctionBrokerChild::GetInstance()->IsDispatchThread()) {
1206
    return PostToDispatchThread(aWinError, aResult, aParameters...);
1207
  }
1208
1209
  if (FunctionBrokerChild::GetInstance()) {
1210
    IpdlTuple sending, returned;
1211
    HOOK_LOG(LogLevel::Debug,
1212
             ("[%s] Client marshaling parameters.", FunctionHookInfoType::mFunctionName.Data()));
1213
    Request::Marshal(sending, aParameters...);
1214
    HOOK_LOG(LogLevel::Info,
1215
             ("[%s] Client sending broker message.", FunctionHookInfoType::mFunctionName.Data()));
1216
    if (FunctionBrokerChild::GetInstance()->SendBrokerFunction(FunctionHookInfoType::FunctionId(), sending,
1217
                                                           &returned)) {
1218
      HOOK_LOG(LogLevel::Debug,
1219
               ("[%s] Client received broker message response.", FunctionHookInfoType::mFunctionName.Data()));
1220
      bool success = Response::Unmarshal(returned, aResult, aParameters...);
1221
      HOOK_LOG(LogLevel::Info,
1222
               ("[%s] Client response unmarshaling: %s.", FunctionHookInfoType::mFunctionName.Data(), SuccessMsg(success)));
1223
#if defined(XP_WIN)
1224
      if (success && Response::Info::ShouldTransmitError(aResult)) {
1225
        uint32_t* winError = returned.Element<UINT32>(returned.NumElements()-1);
1226
        if (!winError) {
1227
          HOOK_LOG(LogLevel::Error,
1228
                    ("[%s] Client failed to unmarshal error code.", FunctionHookInfoType::mFunctionName.Data()));
1229
          return false;
1230
        }
1231
        HOOK_LOG(LogLevel::Debug,
1232
                 ("[%s] Client response unmarshaled error code: %08x.",
1233
                 FunctionHookInfoType::mFunctionName.Data(), *winError));
1234
        aWinError = *winError;
1235
      }
1236
#endif
1237
      return success;
1238
    }
1239
  }
1240
1241
  HOOK_LOG(LogLevel::Error,
1242
            ("[%s] Client failed to broker call.", FunctionHookInfoType::mFunctionName.Data()));
1243
  return false;
1244
}
1245
1246
template <FunctionHookId functionId, typename EHContainer, typename ResultType, typename ... ParamTypes>
1247
bool
1248
FunctionBroker<functionId, ResultType HOOK_CALL (ParamTypes...), EHContainer>::BrokerCallServer(base::ProcessId aClientId, const IpdlTuple &aInTuple,
1249
                  IpdlTuple *aOutTuple, ParamTypes&... aParams) const
1250
{
1251
  HOOK_LOG(LogLevel::Info, ("[%s] Server brokering function.", FunctionHookInfoType::mFunctionName.Data()));
1252
1253
  ServerCallData scd;
1254
  if (!Request::Unmarshal(scd, aInTuple, aParams...)) {
1255
    HOOK_LOG(LogLevel::Info,
1256
             ("[%s] Server failed to unmarshal.", FunctionHookInfoType::mFunctionName.Data()));
1257
    return false;
1258
  }
1259
1260
  // Make sure that this call was legal -- do not execute a call that
1261
  // shouldn't have been brokered in the first place.
1262
  if (!Request::ShouldBroker(SERVER, aParams...)) {
1263
    HOOK_LOG(LogLevel::Error,
1264
             ("[%s] Server rejected brokering request.", FunctionHookInfoType::mFunctionName.Data()));
1265
    return false;
1266
  }
1267
1268
  // Run the function we are brokering.
1269
  HOOK_LOG(LogLevel::Info, ("[%s] Server broker running function.", FunctionHookInfoType::mFunctionName.Data()));
1270
  ResultType ret = RunFunction(FunctionHookInfoType::mOldFunction, aClientId, aParams...);
1271
1272
#if defined(XP_WIN)
1273
  // Record the thread-local error state (before it is changed) if needed.
1274
  uint32_t err = UINT_MAX;
1275
  bool transmitError = Response::Info::ShouldTransmitError(ret);
1276
  if (transmitError) {
1277
    err = ::GetLastError();
1278
    HOOK_LOG(LogLevel::Info,
1279
             ("[%s] Server returning thread error code: %08x.", FunctionHookInfoType::mFunctionName.Data(), err));
1280
  }
1281
#endif
1282
1283
  // Add the result, win thread error and any returned parameters to the returned tuple.
1284
  Response::Marshal(*aOutTuple, ret, aParams...);
1285
#if defined(XP_WIN)
1286
  if (transmitError) {
1287
    aOutTuple->AddElement(err);
1288
  }
1289
#endif
1290
1291
  return true;
1292
}
1293
1294
template <FunctionHookId functionId, typename EHContainer, typename ResultType, typename ... ParamTypes>
1295
bool
1296
FunctionBroker<functionId,ResultType HOOK_CALL (ParamTypes...), EHContainer>::
1297
PostToDispatchThread(uint32_t& aWinError, ResultType& aRet,
1298
                     ParamTypes&... aParameters) const
1299
{
1300
  MOZ_ASSERT(!FunctionBrokerChild::GetInstance()->IsDispatchThread());
1301
  HOOK_LOG(LogLevel::Debug,
1302
           ("Posting broker task '%s' to dispatch thread", FunctionHookInfoType::mFunctionName.Data()));
1303
1304
  // Run PostToDispatchHelper on the dispatch thread.  It will notify our
1305
  // waiting monitor when it is done.
1306
  RefPtr<FDMonitor> monitor(new FDMonitor());
1307
  MonitorAutoLock lock(*monitor);
1308
  bool success = false;
1309
  bool notified = false;
1310
  FunctionBrokerChild::GetInstance()->PostToDispatchThread(
1311
    NewRunnableFunction("FunctionDispatchThreadRunnable", &PostToDispatchHelper,
1312
                        this, monitor, &notified, &success, &aWinError, &aRet,
1313
                        &aParameters...));
1314
1315
  // We wait to be notified, testing that notified was actually set to make
1316
  // sure this isn't a spurious wakeup.
1317
  while (!notified) {
1318
    monitor->Wait();
1319
  }
1320
  return success;
1321
}
1322
1323
void AddBrokeredFunctionHooks(FunctionHookArray& aHooks);
1324
1325
} // namespace plugins
1326
} // namespace mozilla
1327
1328
#endif // dom_plugins_ipc_PluginHooksWin_h