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.
39namespace sandbox {
40
41// this is the assumed channel size. This can be overridden in a given
42// IPC implementation.
43const 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.
52template<typename T>
53class 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.
91template<>
92class 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.
129template<>
130class 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.
183template<>
184class 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.
212template<size_t n>
213class 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.
243class 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.
250template<>
251class 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
319template <typename IPCProvider, typename Par1>
320ResultCode 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.
336template <typename IPCProvider, typename Par1, typename Par2>
337ResultCode 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.
354template <typename IPCProvider, typename Par1, typename Par2, typename Par3>
355ResultCode 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.
374template <typename IPCProvider, typename Par1, typename Par2, typename Par3,
375          typename Par4>
376ResultCode 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.
398template <typename IPCProvider, typename Par1, typename Par2, typename Par3,
399          typename Par4, typename Par5>
400ResultCode 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.
424template <typename IPCProvider, typename Par1, typename Par2, typename Par3,
425          typename Par4, typename Par5, typename Par6>
426ResultCode 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.
452template <typename IPCProvider, typename Par1, typename Par2, typename Par3,
453          typename Par4, typename Par5, typename Par6, typename Par7>
454ResultCode 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__
484