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