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