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 "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__
    484