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