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