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_PARAMS_H__
      6 #define SANDBOX_SRC_CROSSCALL_PARAMS_H__
      7 
      8 #include <windows.h>
      9 #include <lmaccess.h>
     10 #include <stddef.h>
     11 #include <stdint.h>
     12 
     13 #include <memory>
     14 
     15 #include "base/macros.h"
     16 #include "sandbox/win/src/internal_types.h"
     17 #include "sandbox/win/src/sandbox_types.h"
     18 
     19 // Increases |value| until there is no need for padding given an int64_t
     20 // alignment. Returns the increased value.
     21 inline uint32_t Align(uint32_t value) {
     22   uint32_t alignment = sizeof(int64_t);
     23   return ((value + alignment - 1) / alignment) * alignment;
     24 }
     25 
     26 // This header is part of CrossCall: the sandbox inter-process communication.
     27 // This header defines the basic types used both in the client IPC and in the
     28 // server IPC code. CrossCallParams and ActualCallParams model the input
     29 // parameters of an IPC call and CrossCallReturn models the output params and
     30 // the return value.
     31 //
     32 // An IPC call is defined by its 'tag' which is a (uint32_t) unique identifier
     33 // that is used to route the IPC call to the proper server. Every tag implies
     34 // a complete call signature including the order and type of each parameter.
     35 //
     36 // Like most IPC systems. CrossCall is designed to take as inputs 'simple'
     37 // types such as integers and strings. Classes, generic arrays or pointers to
     38 // them are not supported.
     39 //
     40 // Another limitation of CrossCall is that the return value and output
     41 // parameters can only be uint32_t integers. Returning complex structures or
     42 // strings is not supported.
     43 
     44 namespace sandbox {
     45 
     46 // max number of extended return parameters. See CrossCallReturn
     47 const size_t kExtendedReturnCount = 8;
     48 
     49 // Union of multiple types to be used as extended results
     50 // in the CrossCallReturn.
     51 union MultiType {
     52   uint32_t unsigned_int;
     53   void* pointer;
     54   HANDLE handle;
     55   ULONG_PTR ulong_ptr;
     56 };
     57 
     58 // Maximum number of IPC parameters currently supported.
     59 // To increase this value, we have to:
     60 //  - Add another Callback typedef to Dispatcher.
     61 //  - Add another case to the switch on SharedMemIPCServer::InvokeCallback.
     62 //  - Add another case to the switch in GetActualAndMaxBufferSize
     63 const int kMaxIpcParams = 9;
     64 
     65 // Contains the information about a parameter in the ipc buffer.
     66 struct ParamInfo {
     67   ArgType type_;
     68   uint32_t offset_;
     69   uint32_t size_;
     70 };
     71 
     72 // Models the return value and the return parameters of an IPC call
     73 // currently limited to one status code and eight generic return values
     74 // which cannot be pointers to other data. For x64 ports this structure
     75 // might have to use other integer types.
     76 struct CrossCallReturn {
     77   // the IPC tag. It should match the original IPC tag.
     78   uint32_t tag;
     79   // The result of the IPC operation itself.
     80   ResultCode call_outcome;
     81   // the result of the IPC call as executed in the server. The interpretation
     82   // of this value depends on the specific service.
     83   union {
     84     NTSTATUS nt_status;
     85     DWORD    win32_result;
     86   };
     87   // Number of extended return values.
     88   uint32_t extended_count;
     89   // for calls that should return a windows handle. It is found here.
     90   HANDLE handle;
     91   // The array of extended values.
     92   MultiType extended[kExtendedReturnCount];
     93 };
     94 
     95 // CrossCallParams base class that models the input params all packed in a
     96 // single compact memory blob. The representation can vary but in general a
     97 // given child of this class is meant to represent all input parameters
     98 // necessary to make a IPC call.
     99 //
    100 // This class cannot have virtual members because its assumed the IPC
    101 // parameters start from the 'this' pointer to the end, which is defined by
    102 // one of the subclasses
    103 //
    104 // Objects of this class cannot be constructed directly. Only derived
    105 // classes have the proper knowledge to construct it.
    106 class CrossCallParams {
    107  public:
    108   // Returns the tag (ipc unique id) associated with this IPC.
    109   uint32_t GetTag() const { return tag_; }
    110 
    111   // Returns the beggining of the buffer where the IPC params can be stored.
    112   // prior to an IPC call
    113   const void* GetBuffer() const {
    114     return this;
    115   }
    116 
    117   // Returns how many parameter this IPC call should have.
    118   uint32_t GetParamsCount() const { return params_count_; }
    119 
    120   // Returns a pointer to the CrossCallReturn structure.
    121   CrossCallReturn* GetCallReturn() {
    122     return &call_return;
    123   }
    124 
    125   // Returns TRUE if this call contains InOut parameters.
    126   bool IsInOut() const { return (1 == is_in_out_); }
    127 
    128   // Tells the CrossCall object if it contains InOut parameters.
    129   void SetIsInOut(bool value) {
    130     if (value)
    131       is_in_out_ = 1;
    132     else
    133       is_in_out_ = 0;
    134   }
    135 
    136  protected:
    137   // constructs the IPC call params. Called only from the derived classes
    138   CrossCallParams(uint32_t tag, uint32_t params_count)
    139       : tag_(tag), is_in_out_(0), params_count_(params_count) {}
    140 
    141  private:
    142   uint32_t tag_;
    143   uint32_t is_in_out_;
    144   CrossCallReturn call_return;
    145   const uint32_t params_count_;
    146   DISALLOW_COPY_AND_ASSIGN(CrossCallParams);
    147 };
    148 
    149 // ActualCallParams models an specific IPC call parameters with respect to the
    150 // storage allocation that the packed parameters should need.
    151 // NUMBER_PARAMS: the number of parameters, valid from 1 to N
    152 // BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take,
    153 // typically the block size is defined by the channel size of the underlying
    154 // ipc mechanism.
    155 // In practice this class is used to levergage C++ capacity to properly
    156 // calculate sizes and displacements given the possibility of the packed params
    157 // blob to be complex.
    158 //
    159 // As is, this class assumes that the layout of the blob is as follows. Assume
    160 // that NUMBER_PARAMS = 2 and a 32-bit build:
    161 //
    162 // [ tag                4 bytes]
    163 // [ IsOnOut            4 bytes]
    164 // [ call return       52 bytes]
    165 // [ params count       4 bytes]
    166 // [ parameter 0 type   4 bytes]
    167 // [ parameter 0 offset 4 bytes] ---delta to ---\
    168 // [ parameter 0 size   4 bytes]                |
    169 // [ parameter 1 type   4 bytes]                |
    170 // [ parameter 1 offset 4 bytes] ---------------|--\
    171 // [ parameter 1 size   4 bytes]                |  |
    172 // [ parameter 2 type   4 bytes]                |  |
    173 // [ parameter 2 offset 4 bytes] ----------------------\
    174 // [ parameter 2 size   4 bytes]                |  |   |
    175 // |---------------------------|                |  |   |
    176 // | value 0     (x bytes)     | <--------------/  |   |
    177 // | value 1     (y bytes)     | <-----------------/   |
    178 // |                           |                       |
    179 // | end of buffer             | <---------------------/
    180 // |---------------------------|
    181 //
    182 // Note that the actual number of params is NUMBER_PARAMS + 1
    183 // so that the size of each actual param can be computed from the difference
    184 // between one parameter and the next down. The offset of the last param
    185 // points to the end of the buffer and the type and size are undefined.
    186 //
    187 template <size_t NUMBER_PARAMS, size_t BLOCK_SIZE>
    188 class ActualCallParams : public CrossCallParams {
    189  public:
    190   // constructor. Pass the ipc unique tag as input
    191   explicit ActualCallParams(uint32_t tag)
    192       : CrossCallParams(tag, NUMBER_PARAMS) {
    193     param_info_[0].offset_ =
    194         static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
    195   }
    196 
    197   // Testing-only constructor. Allows setting the |number_params| to a
    198   // wrong value.
    199   ActualCallParams(uint32_t tag, uint32_t number_params)
    200       : CrossCallParams(tag, number_params) {
    201     param_info_[0].offset_ =
    202         static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
    203   }
    204 
    205   // Testing-only method. Allows setting the apparent size to a wrong value.
    206   // returns the previous size.
    207   uint32_t OverrideSize(uint32_t new_size) {
    208     uint32_t previous_size = param_info_[NUMBER_PARAMS].offset_;
    209     param_info_[NUMBER_PARAMS].offset_ = new_size;
    210     return previous_size;
    211   }
    212 
    213   // Copies each paramter into the internal buffer. For each you must supply:
    214   // index: 0 for the first param, 1 for the next an so on
    215   bool CopyParamIn(uint32_t index,
    216                    const void* parameter_address,
    217                    uint32_t size,
    218                    bool is_in_out,
    219                    ArgType type) {
    220     if (index >= NUMBER_PARAMS) {
    221       return false;
    222     }
    223 
    224     if (UINT32_MAX == size) {
    225       // Memory error while getting the size.
    226       return false;
    227     }
    228 
    229     if (size && !parameter_address) {
    230       return false;
    231     }
    232 
    233     if ((size > sizeof(*this)) ||
    234         (param_info_[index].offset_ > (sizeof(*this) - size))) {
    235       // It does not fit, abort copy.
    236       return false;
    237     }
    238 
    239     char* dest = reinterpret_cast<char*>(this) +  param_info_[index].offset_;
    240 
    241     // We might be touching user memory, this has to be done from inside a try
    242     // except.
    243     __try {
    244       memcpy(dest, parameter_address, size);
    245     }
    246     __except(EXCEPTION_EXECUTE_HANDLER) {
    247       return false;
    248     }
    249 
    250     // Set the flag to tell the broker to update the buffer once the call is
    251     // made.
    252     if (is_in_out)
    253       SetIsInOut(true);
    254 
    255     param_info_[index + 1].offset_ = Align(param_info_[index].offset_ +
    256                                                 size);
    257     param_info_[index].size_ = size;
    258     param_info_[index].type_ = type;
    259     return true;
    260   }
    261 
    262   // Returns a pointer to a parameter in the memory section.
    263   void* GetParamPtr(size_t index) {
    264     return reinterpret_cast<char*>(this) + param_info_[index].offset_;
    265   }
    266 
    267   // Returns the total size of the buffer. Only valid once all the paramters
    268   // have been copied in with CopyParamIn.
    269   uint32_t GetSize() const { return param_info_[NUMBER_PARAMS].offset_; }
    270 
    271  protected:
    272   ActualCallParams() : CrossCallParams(0, NUMBER_PARAMS) { }
    273 
    274  private:
    275   ParamInfo param_info_[NUMBER_PARAMS + 1];
    276   char parameters_[BLOCK_SIZE - sizeof(CrossCallParams)
    277                    - sizeof(ParamInfo) * (NUMBER_PARAMS + 1)];
    278   DISALLOW_COPY_AND_ASSIGN(ActualCallParams);
    279 };
    280 
    281 static_assert(sizeof(ActualCallParams<1, 1024>) == 1024, "bad size buffer");
    282 static_assert(sizeof(ActualCallParams<2, 1024>) == 1024, "bad size buffer");
    283 static_assert(sizeof(ActualCallParams<3, 1024>) == 1024, "bad size buffer");
    284 
    285 }  // namespace sandbox
    286 
    287 #endif  // SANDBOX_SRC_CROSSCALL_PARAMS_H__
    288