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