OLD | NEW |
| (Empty) |
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: | |
58 CopyHelper(const T& t) : t_(t) {} | |
59 | |
60 // Returns the pointer to the start of the input. | |
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. | |
67 bool Update(void* buffer) { | |
68 // Not supported; | |
69 return true; | |
70 } | |
71 | |
72 // Returns the size of the input in bytes. | |
73 uint32_t GetSize() const { return sizeof(T); } | |
74 | |
75 // Returns true if the current type is used as an In or InOut parameter. | |
76 bool IsInOut() { | |
77 return false; | |
78 } | |
79 | |
80 // Returns this object's type. | |
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: | |
95 CopyHelper(void* t) : t_(t) {} | |
96 | |
97 // Returns the pointer to the start of the input. | |
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. | |
104 bool Update(void* buffer) { | |
105 // Not supported; | |
106 return true; | |
107 } | |
108 | |
109 // Returns the size of the input in bytes. | |
110 uint32_t GetSize() const { return sizeof(t_); } | |
111 | |
112 // Returns true if the current type is used as an In or InOut parameter. | |
113 bool IsInOut() { | |
114 return false; | |
115 } | |
116 | |
117 // Returns this object's type. | |
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: | |
131 CopyHelper(const wchar_t* t) | |
132 : t_(t) { | |
133 } | |
134 | |
135 // Returns the pointer to the start of the string. | |
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. | |
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. | |
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. | |
160 bool IsInOut() { | |
161 return false; | |
162 } | |
163 | |
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. | |
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; | |
187 CopyHelper(wchar_t* t) : Base(t) {} | |
188 | |
189 const void* GetStart() const { | |
190 return Base::GetStart(); | |
191 } | |
192 | |
193 bool Update(void* buffer) { | |
194 return Base::Update(buffer); | |
195 } | |
196 | |
197 uint32_t GetSize() const { return Base::GetSize(); } | |
198 | |
199 bool IsInOut() { | |
200 return Base::IsInOut(); | |
201 } | |
202 | |
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; | |
215 CopyHelper(array t) : Base(t) {} | |
216 | |
217 const void* GetStart() const { | |
218 return Base::GetStart(); | |
219 } | |
220 | |
221 bool Update(void* buffer) { | |
222 return Base::Update(buffer); | |
223 } | |
224 | |
225 uint32_t GetSize() const { return Base::GetSize(); } | |
226 | |
227 bool IsInOut() { | |
228 return Base::IsInOut(); | |
229 } | |
230 | |
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: | |
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: | |
250 CopyHelper(const InOutCountedBuffer t) : t_(t) {} | |
251 | |
252 // Returns the pointer to the start of the string. | |
253 const void* GetStart() const { | |
254 return t_.Buffer(); | |
255 } | |
256 | |
257 // Updates the buffer with the value from the new buffer in parameter. | |
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. | |
272 uint32_t GetSize() const { return t_.Size(); } | |
273 | |
274 // Returns true if the current type is used as an In or InOut parameter. | |
275 bool IsInOut() { | |
276 return true; | |
277 } | |
278 | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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__ | |
OLD | NEW |