1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef SANDBOX_SRC_CROSSCALL_CLIENT_H_
6 #define SANDBOX_SRC_CROSSCALL_CLIENT_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include "base/compiler_specific.h"
12 #include "sandbox/win/src/crosscall_params.h"
13 #include "sandbox/win/src/sandbox.h"
14 
15 // This header defines the CrossCall(..) family of templated functions
16 // Their purpose is to simulate the syntax of regular call but to generate
17 // and IPC from the client-side.
18 //
19 // The basic pattern is to
20 //   1) use template argument deduction to compute the size of each
21 //      parameter and the appropriate copy method
22 //   2) pack the parameters in the appropriate ActualCallParams< > object
23 //   3) call the IPC interface IPCProvider::DoCall( )
24 //
25 // The general interface of CrossCall is:
26 //  ResultCode CrossCall(IPCProvider& ipc_provider,
27 //                       uint32_t tag,
28 //                       const Par1& p1, const Par2& p2,...pn
29 //                       CrossCallReturn* answer)
30 //
31 //  where:
32 //    ipc_provider: is a specific implementation of the ipc transport see
33 //                  sharedmem_ipc_server.h for an example.
34 //    tag : is the unique id for this IPC call. Is used to route the call to
35 //          the appropriate service.
36 //    p1, p2,.. pn : The input parameters of the IPC. Use only simple types
37 //                   and wide strings (can add support for others).
38 //    answer : If the IPC was successful. The server-side answer is here. The
39 //             interpretation of the answer is private to client and server.
40 //
41 // The return value is ALL_OK if the IPC was delivered to the server, other
42 // return codes indicate that the IPC transport failed to deliver it.
43 namespace sandbox {
44 
45 enum class IpcTag;
46 
47 // The copy helper uses templates to deduce the appropriate copy function to
48 // copy the input parameters in the buffer that is going to be send across the
49 // IPC. These template facility can be made more sophisticated as need arises.
50 
51 // The default copy helper. It catches the general case where no other
52 // specialized template matches better. We set the type to UINT32_TYPE, so this
53 // only works with objects whose size is 32 bits.
54 template <typename T>
55 class CopyHelper {
56  public:
CopyHelper(const T & t)57   CopyHelper(const T& t) : t_(t) {}
58 
59   // Returns the pointer to the start of the input.
GetStart()60   const void* GetStart() const { return &t_; }
61 
62   // Update the stored value with the value in the buffer. This is not
63   // supported for this type.
Update(void * buffer)64   bool Update(void* buffer) {
65     // Not supported;
66     return true;
67   }
68 
69   // Returns the size of the input in bytes.
GetSize()70   uint32_t GetSize() const { return sizeof(T); }
71 
72   // Returns true if the current type is used as an In or InOut parameter.
IsInOut()73   bool IsInOut() { return false; }
74 
75   // Returns this object's type.
GetType()76   ArgType GetType() {
77     static_assert(sizeof(T) == sizeof(uint32_t), "specialization needed");
78     return UINT32_TYPE;
79   }
80 
81  private:
82   const T& t_;
83 };
84 
85 // This copy helper template specialization if for the void pointer
86 // case both 32 and 64 bit.
87 template <>
88 class CopyHelper<void*> {
89  public:
CopyHelper(void * t)90   CopyHelper(void* t) : t_(t) {}
91 
92   // Returns the pointer to the start of the input.
GetStart()93   const void* GetStart() const { return &t_; }
94 
95   // Update the stored value with the value in the buffer. This is not
96   // supported for this type.
Update(void * buffer)97   bool Update(void* buffer) {
98     // Not supported;
99     return true;
100   }
101 
102   // Returns the size of the input in bytes.
GetSize()103   uint32_t GetSize() const { return sizeof(t_); }
104 
105   // Returns true if the current type is used as an In or InOut parameter.
IsInOut()106   bool IsInOut() { return false; }
107 
108   // Returns this object's type.
GetType()109   ArgType GetType() { return VOIDPTR_TYPE; }
110 
111  private:
112   const void* t_;
113 };
114 
115 // This copy helper template specialization catches the cases where the
116 // parameter is a pointer to a string.
117 template <>
118 class CopyHelper<const wchar_t*> {
119  public:
CopyHelper(const wchar_t * t)120   CopyHelper(const wchar_t* t) : t_(t) {}
121 
122   // Returns the pointer to the start of the string.
GetStart()123   const void* GetStart() const { return t_; }
124 
125   // Update the stored value with the value in the buffer. This is not
126   // supported for this type.
Update(void * buffer)127   bool Update(void* buffer) {
128     // Not supported;
129     return true;
130   }
131 
132   // Returns the size of the string in bytes. We define a nullptr string to
133   // be of zero length.
GetSize()134   uint32_t GetSize() const {
135     __try {
136       return (!t_) ? 0
137                    : static_cast<uint32_t>(StringLength(t_) * sizeof(t_[0]));
138     } __except (EXCEPTION_EXECUTE_HANDLER) {
139       return UINT32_MAX;
140     }
141   }
142 
143   // Returns true if the current type is used as an In or InOut parameter.
IsInOut()144   bool IsInOut() { return false; }
145 
GetType()146   ArgType GetType() { return WCHAR_TYPE; }
147 
148  private:
149   // We provide our not very optimized version of wcslen(), since we don't
150   // want to risk having the linker use the version in the CRT since the CRT
151   // might not be present when we do an early IPC call.
StringLength(const wchar_t * wcs)152   static size_t CDECL StringLength(const wchar_t* wcs) {
153     const wchar_t* eos = wcs;
154     while (*eos++)
155       ;
156     return static_cast<size_t>(eos - wcs - 1);
157   }
158 
159   const wchar_t* t_;
160 };
161 
162 // Specialization for non-const strings. We just reuse the implementation of the
163 // const string specialization.
164 template <>
165 class CopyHelper<wchar_t*> : public CopyHelper<const wchar_t*> {
166  public:
167   typedef CopyHelper<const wchar_t*> Base;
CopyHelper(wchar_t * t)168   CopyHelper(wchar_t* t) : Base(t) {}
169 
GetStart()170   const void* GetStart() const { return Base::GetStart(); }
171 
Update(void * buffer)172   bool Update(void* buffer) { return Base::Update(buffer); }
173 
GetSize()174   uint32_t GetSize() const { return Base::GetSize(); }
175 
IsInOut()176   bool IsInOut() { return Base::IsInOut(); }
177 
GetType()178   ArgType GetType() { return Base::GetType(); }
179 };
180 
181 // Specialization for wchar_t arrays strings. We just reuse the implementation
182 // of the const string specialization.
183 template <size_t n>
184 class CopyHelper<const wchar_t[n]> : public CopyHelper<const wchar_t*> {
185  public:
186   typedef const wchar_t array[n];
187   typedef CopyHelper<const wchar_t*> Base;
CopyHelper(array t)188   CopyHelper(array t) : Base(t) {}
189 
GetStart()190   const void* GetStart() const { return Base::GetStart(); }
191 
Update(void * buffer)192   bool Update(void* buffer) { return Base::Update(buffer); }
193 
GetSize()194   uint32_t GetSize() const { return Base::GetSize(); }
195 
IsInOut()196   bool IsInOut() { return Base::IsInOut(); }
197 
GetType()198   ArgType GetType() { return Base::GetType(); }
199 };
200 
201 // Generic encapsulation class containing a pointer to a buffer and the
202 // size of the buffer. It is used by the IPC to be able to pass in/out
203 // parameters.
204 class InOutCountedBuffer : public CountedBuffer {
205  public:
InOutCountedBuffer(void * buffer,uint32_t size)206   InOutCountedBuffer(void* buffer, uint32_t size)
207       : CountedBuffer(buffer, size) {}
208 };
209 
210 // This copy helper template specialization catches the cases where the
211 // parameter is a an input buffer.
212 template <>
213 class CopyHelper<CountedBuffer> {
214  public:
CopyHelper(const CountedBuffer t)215   CopyHelper(const CountedBuffer t) : t_(t) {}
216 
217   // Returns the pointer to the start of the string.
GetStart()218   const void* GetStart() const { return t_.Buffer(); }
219 
220   // Update not required so just return true;
Update(void * buffer)221   bool Update(void* buffer) { return true; }
222 
223   // Returns the size of the string in bytes. We define a nullptr string to
224   // be of zero length.
GetSize()225   uint32_t GetSize() const { return t_.Size(); }
226 
227   // Returns true if the current type is used as an In or InOut parameter.
IsInOut()228   bool IsInOut() { return false; }
229 
GetType()230   ArgType GetType() { return INPTR_TYPE; }
231 
232  private:
233   const CountedBuffer t_;
234 };
235 
236 // This copy helper template specialization catches the cases where the
237 // parameter is a an input/output buffer.
238 template <>
239 class CopyHelper<InOutCountedBuffer> {
240  public:
CopyHelper(const InOutCountedBuffer t)241   CopyHelper(const InOutCountedBuffer t) : t_(t) {}
242 
243   // Returns the pointer to the start of the string.
GetStart()244   const void* GetStart() const { return t_.Buffer(); }
245 
246   // Updates the buffer with the value from the new buffer in parameter.
Update(void * buffer)247   bool Update(void* buffer) {
248     // We are touching user memory, this has to be done from inside a try
249     // except.
250     __try {
251       memcpy_wrapper(t_.Buffer(), buffer, t_.Size());
252     } __except (EXCEPTION_EXECUTE_HANDLER) {
253       return false;
254     }
255     return true;
256   }
257 
258   // Returns the size of the string in bytes. We define a nullptr string to
259   // be of zero length.
GetSize()260   uint32_t GetSize() const { return t_.Size(); }
261 
262   // Returns true if the current type is used as an In or InOut parameter.
IsInOut()263   bool IsInOut() { return true; }
264 
GetType()265   ArgType GetType() { return INOUTPTR_TYPE; }
266 
267  private:
268   const InOutCountedBuffer t_;
269 };
270 
271 // The following two macros make it less error prone the generation
272 // of CrossCall functions with ever more input parameters.
273 
274 #define XCALL_GEN_PARAMS_OBJ(num, params)                      \
275   typedef ActualCallParams<num, kIPCChannelSize> ActualParams; \
276   void* raw_mem = ipc_provider.GetBuffer();                    \
277   if (!raw_mem)                                                \
278     return SBOX_ERROR_NO_SPACE;                                \
279   ActualParams* params = new (raw_mem) ActualParams(tag);
280 
281 #define XCALL_GEN_COPY_PARAM(num, params)                                  \
282   static_assert(kMaxIpcParams >= num, "too many parameters");              \
283   CopyHelper<Par##num> ch##num(p##num);                                    \
284   if (!params->CopyParamIn(num - 1, ch##num.GetStart(), ch##num.GetSize(), \
285                            ch##num.IsInOut(), ch##num.GetType()))          \
286     return SBOX_ERROR_NO_SPACE;
287 
288 #define XCALL_GEN_UPDATE_PARAM(num, params)            \
289   if (!ch##num.Update(params->GetParamPtr(num - 1))) { \
290     ipc_provider.FreeBuffer(raw_mem);                  \
291     return SBOX_ERROR_BAD_PARAMS;                      \
292   }
293 
294 #define XCALL_GEN_FREE_CHANNEL() ipc_provider.FreeBuffer(raw_mem);
295 
296 // CrossCall template with one input parameter
297 template <typename IPCProvider, typename Par1>
CrossCall(IPCProvider & ipc_provider,IpcTag tag,const Par1 & p1,CrossCallReturn * answer)298 ResultCode CrossCall(IPCProvider& ipc_provider,
299                      IpcTag tag,
300                      const Par1& p1,
301                      CrossCallReturn* answer) {
302   XCALL_GEN_PARAMS_OBJ(1, call_params);
303   XCALL_GEN_COPY_PARAM(1, call_params);
304 
305   ResultCode result = ipc_provider.DoCall(call_params, answer);
306 
307   if (SBOX_ERROR_CHANNEL_ERROR != result) {
308     XCALL_GEN_UPDATE_PARAM(1, call_params);
309     XCALL_GEN_FREE_CHANNEL();
310   }
311 
312   return result;
313 }
314 
315 // CrossCall template with two input parameters.
316 template <typename IPCProvider, typename Par1, typename Par2>
CrossCall(IPCProvider & ipc_provider,IpcTag tag,const Par1 & p1,const Par2 & p2,CrossCallReturn * answer)317 ResultCode CrossCall(IPCProvider& ipc_provider,
318                      IpcTag tag,
319                      const Par1& p1,
320                      const Par2& p2,
321                      CrossCallReturn* answer) {
322   XCALL_GEN_PARAMS_OBJ(2, call_params);
323   XCALL_GEN_COPY_PARAM(1, call_params);
324   XCALL_GEN_COPY_PARAM(2, call_params);
325 
326   ResultCode result = ipc_provider.DoCall(call_params, answer);
327 
328   if (SBOX_ERROR_CHANNEL_ERROR != result) {
329     XCALL_GEN_UPDATE_PARAM(1, call_params);
330     XCALL_GEN_UPDATE_PARAM(2, call_params);
331     XCALL_GEN_FREE_CHANNEL();
332   }
333   return result;
334 }
335 
336 // CrossCall template with three input parameters.
337 template <typename IPCProvider, typename Par1, typename Par2, typename Par3>
CrossCall(IPCProvider & ipc_provider,IpcTag tag,const Par1 & p1,const Par2 & p2,const Par3 & p3,CrossCallReturn * answer)338 ResultCode CrossCall(IPCProvider& ipc_provider,
339                      IpcTag tag,
340                      const Par1& p1,
341                      const Par2& p2,
342                      const Par3& p3,
343                      CrossCallReturn* answer) {
344   XCALL_GEN_PARAMS_OBJ(3, call_params);
345   XCALL_GEN_COPY_PARAM(1, call_params);
346   XCALL_GEN_COPY_PARAM(2, call_params);
347   XCALL_GEN_COPY_PARAM(3, call_params);
348 
349   ResultCode result = ipc_provider.DoCall(call_params, answer);
350 
351   if (SBOX_ERROR_CHANNEL_ERROR != result) {
352     XCALL_GEN_UPDATE_PARAM(1, call_params);
353     XCALL_GEN_UPDATE_PARAM(2, call_params);
354     XCALL_GEN_UPDATE_PARAM(3, call_params);
355     XCALL_GEN_FREE_CHANNEL();
356   }
357   return result;
358 }
359 
360 // CrossCall template with four input parameters.
361 template <typename IPCProvider,
362           typename Par1,
363           typename Par2,
364           typename Par3,
365           typename Par4>
CrossCall(IPCProvider & ipc_provider,IpcTag tag,const Par1 & p1,const Par2 & p2,const Par3 & p3,const Par4 & p4,CrossCallReturn * answer)366 ResultCode CrossCall(IPCProvider& ipc_provider,
367                      IpcTag tag,
368                      const Par1& p1,
369                      const Par2& p2,
370                      const Par3& p3,
371                      const Par4& p4,
372                      CrossCallReturn* answer) {
373   XCALL_GEN_PARAMS_OBJ(4, call_params);
374   XCALL_GEN_COPY_PARAM(1, call_params);
375   XCALL_GEN_COPY_PARAM(2, call_params);
376   XCALL_GEN_COPY_PARAM(3, call_params);
377   XCALL_GEN_COPY_PARAM(4, call_params);
378 
379   ResultCode result = ipc_provider.DoCall(call_params, answer);
380 
381   if (SBOX_ERROR_CHANNEL_ERROR != result) {
382     XCALL_GEN_UPDATE_PARAM(1, call_params);
383     XCALL_GEN_UPDATE_PARAM(2, call_params);
384     XCALL_GEN_UPDATE_PARAM(3, call_params);
385     XCALL_GEN_UPDATE_PARAM(4, call_params);
386     XCALL_GEN_FREE_CHANNEL();
387   }
388   return result;
389 }
390 
391 // CrossCall template with five input parameters.
392 template <typename IPCProvider,
393           typename Par1,
394           typename Par2,
395           typename Par3,
396           typename Par4,
397           typename Par5>
CrossCall(IPCProvider & ipc_provider,IpcTag tag,const Par1 & p1,const Par2 & p2,const Par3 & p3,const Par4 & p4,const Par5 & p5,CrossCallReturn * answer)398 ResultCode CrossCall(IPCProvider& ipc_provider,
399                      IpcTag tag,
400                      const Par1& p1,
401                      const Par2& p2,
402                      const Par3& p3,
403                      const Par4& p4,
404                      const Par5& p5,
405                      CrossCallReturn* answer) {
406   XCALL_GEN_PARAMS_OBJ(5, call_params);
407   XCALL_GEN_COPY_PARAM(1, call_params);
408   XCALL_GEN_COPY_PARAM(2, call_params);
409   XCALL_GEN_COPY_PARAM(3, call_params);
410   XCALL_GEN_COPY_PARAM(4, call_params);
411   XCALL_GEN_COPY_PARAM(5, call_params);
412 
413   ResultCode result = ipc_provider.DoCall(call_params, answer);
414 
415   if (SBOX_ERROR_CHANNEL_ERROR != result) {
416     XCALL_GEN_UPDATE_PARAM(1, call_params);
417     XCALL_GEN_UPDATE_PARAM(2, call_params);
418     XCALL_GEN_UPDATE_PARAM(3, call_params);
419     XCALL_GEN_UPDATE_PARAM(4, call_params);
420     XCALL_GEN_UPDATE_PARAM(5, call_params);
421     XCALL_GEN_FREE_CHANNEL();
422   }
423   return result;
424 }
425 
426 // CrossCall template with six input parameters.
427 template <typename IPCProvider,
428           typename Par1,
429           typename Par2,
430           typename Par3,
431           typename Par4,
432           typename Par5,
433           typename Par6>
CrossCall(IPCProvider & ipc_provider,IpcTag tag,const Par1 & p1,const Par2 & p2,const Par3 & p3,const Par4 & p4,const Par5 & p5,const Par6 & p6,CrossCallReturn * answer)434 ResultCode CrossCall(IPCProvider& ipc_provider,
435                      IpcTag tag,
436                      const Par1& p1,
437                      const Par2& p2,
438                      const Par3& p3,
439                      const Par4& p4,
440                      const Par5& p5,
441                      const Par6& p6,
442                      CrossCallReturn* answer) {
443   XCALL_GEN_PARAMS_OBJ(6, call_params);
444   XCALL_GEN_COPY_PARAM(1, call_params);
445   XCALL_GEN_COPY_PARAM(2, call_params);
446   XCALL_GEN_COPY_PARAM(3, call_params);
447   XCALL_GEN_COPY_PARAM(4, call_params);
448   XCALL_GEN_COPY_PARAM(5, call_params);
449   XCALL_GEN_COPY_PARAM(6, call_params);
450 
451   ResultCode result = ipc_provider.DoCall(call_params, answer);
452 
453   if (SBOX_ERROR_CHANNEL_ERROR != result) {
454     XCALL_GEN_UPDATE_PARAM(1, call_params);
455     XCALL_GEN_UPDATE_PARAM(2, call_params);
456     XCALL_GEN_UPDATE_PARAM(3, call_params);
457     XCALL_GEN_UPDATE_PARAM(4, call_params);
458     XCALL_GEN_UPDATE_PARAM(5, call_params);
459     XCALL_GEN_UPDATE_PARAM(6, call_params);
460     XCALL_GEN_FREE_CHANNEL();
461   }
462   return result;
463 }
464 
465 // CrossCall template with seven input parameters.
466 template <typename IPCProvider,
467           typename Par1,
468           typename Par2,
469           typename Par3,
470           typename Par4,
471           typename Par5,
472           typename Par6,
473           typename Par7>
CrossCall(IPCProvider & ipc_provider,IpcTag tag,const Par1 & p1,const Par2 & p2,const Par3 & p3,const Par4 & p4,const Par5 & p5,const Par6 & p6,const Par7 & p7,CrossCallReturn * answer)474 ResultCode CrossCall(IPCProvider& ipc_provider,
475                      IpcTag tag,
476                      const Par1& p1,
477                      const Par2& p2,
478                      const Par3& p3,
479                      const Par4& p4,
480                      const Par5& p5,
481                      const Par6& p6,
482                      const Par7& p7,
483                      CrossCallReturn* answer) {
484   XCALL_GEN_PARAMS_OBJ(7, call_params);
485   XCALL_GEN_COPY_PARAM(1, call_params);
486   XCALL_GEN_COPY_PARAM(2, call_params);
487   XCALL_GEN_COPY_PARAM(3, call_params);
488   XCALL_GEN_COPY_PARAM(4, call_params);
489   XCALL_GEN_COPY_PARAM(5, call_params);
490   XCALL_GEN_COPY_PARAM(6, call_params);
491   XCALL_GEN_COPY_PARAM(7, call_params);
492 
493   ResultCode result = ipc_provider.DoCall(call_params, answer);
494 
495   if (SBOX_ERROR_CHANNEL_ERROR != result) {
496     XCALL_GEN_UPDATE_PARAM(1, call_params);
497     XCALL_GEN_UPDATE_PARAM(2, call_params);
498     XCALL_GEN_UPDATE_PARAM(3, call_params);
499     XCALL_GEN_UPDATE_PARAM(4, call_params);
500     XCALL_GEN_UPDATE_PARAM(5, call_params);
501     XCALL_GEN_UPDATE_PARAM(6, call_params);
502     XCALL_GEN_UPDATE_PARAM(7, call_params);
503     XCALL_GEN_FREE_CHANNEL();
504   }
505   return result;
506 }
507 }  // namespace sandbox
508 
509 #endif  // SANDBOX_SRC_CROSSCALL_CLIENT_H__
510