/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, ¶mString); |
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, ¬ified, &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 |