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