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