Home | History | Annotate | Download | only in src
      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__
    527