1 #pragma once 2 // IWYU pragma: private, include "rlbox.hpp" 3 // IWYU pragma: friend "rlbox_.*\.hpp" 4 5 #include <algorithm> 6 #include <atomic> 7 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 8 # include <chrono> 9 #endif 10 #include <cstdlib> 11 #include <map> 12 #include <mutex> 13 #ifndef RLBOX_USE_CUSTOM_SHARED_LOCK 14 # include <shared_mutex> 15 #endif 16 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 17 # include <sstream> 18 # include <string> 19 #endif 20 #include <type_traits> 21 #include <utility> 22 #include <vector> 23 24 #include "rlbox_conversion.hpp" 25 #include "rlbox_helpers.hpp" 26 #include "rlbox_stdlib_polyfill.hpp" 27 #include "rlbox_struct_support.hpp" 28 #include "rlbox_type_traits.hpp" 29 #include "rlbox_wrapper_traits.hpp" 30 31 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 32 using namespace std::chrono; 33 #endif 34 35 namespace rlbox { 36 37 namespace convert_fn_ptr_to_sandbox_equivalent_detail { 38 template<typename T, typename T_Sbx> 39 using conv = ::rlbox::detail::convert_to_sandbox_equivalent_t<T, T_Sbx>; 40 41 template<typename T_Ret, typename... T_Args> 42 using T_Func = T_Ret (*)(T_Args...); 43 44 template<typename T_Sbx, typename T_Ret, typename... T_Args> 45 T_Func<conv<T_Ret, T_Sbx>, conv<T_Args, T_Sbx>...> helper( 46 T_Ret (*)(T_Args...)); 47 } 48 49 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 50 enum class rlbox_transition 51 { 52 INVOKE, 53 CALLBACK 54 }; 55 struct rlbox_transition_timing 56 { 57 rlbox_transition invoke; 58 const char* name; 59 void* ptr; 60 int64_t time; 61 to_stringrlbox::rlbox_transition_timing62 std::string to_string() 63 { 64 std::ostringstream ret; 65 if (invoke == rlbox_transition::INVOKE) { 66 ret << name; 67 } else { 68 ret << "Callback " << ptr; 69 } 70 ret << " : " << time << "\n"; 71 72 return ret.str(); 73 } 74 }; 75 #endif 76 77 #ifndef RLBOX_SINGLE_THREADED_INVOCATIONS 78 # error \ 79 "RLBox does not yet support threading. Please define RLBOX_SINGLE_THREADED_INVOCATIONS prior to including RLBox and ensure you are only using it from a single thread. If threading is required, please file a bug." 80 #endif 81 82 /** 83 * @brief Encapsulation for sandboxes. 84 * 85 * @tparam T_Sbx Type of sandbox. For the null sandbox this is 86 * `rlbox_noop_sandbox` 87 */ 88 template<typename T_Sbx> 89 class rlbox_sandbox : protected T_Sbx 90 { 91 KEEP_CLASSES_FRIENDLY 92 93 private: 94 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 95 std::vector<rlbox_transition_timing> transition_times; 96 #endif 97 98 static inline RLBOX_SHARED_LOCK(sandbox_list_lock); 99 // The actual type of the vector is std::vector<rlbox_sandbox<T_Sbx>*> 100 // However clang 5, 6 have bugs where compilation seg-faults on this type 101 // So we just use this std::vector<void*> 102 static inline std::vector<void*> sandbox_list; 103 104 RLBOX_SHARED_LOCK(func_ptr_cache_lock); 105 std::map<std::string, void*> func_ptr_map; 106 107 // This variable tracks of the sandbox has already been created/destroyed. 108 // APIs in this class should be called only when the sandbox is created. 109 // However, it is expensive to check in APIs such as invoke or in the callback 110 // interceptor. What's more, there could be time of check time of use issues 111 // in the checks as well. 112 // In general, we leave it up to the user to ensure these APIs are never 113 // called prior to sandbox construction or after destruction. We perform some 114 // conservative sanity checks, where they would not add too much overhead. 115 enum class Sandbox_Status 116 { 117 NOT_CREATED, 118 INITIALIZING, 119 CREATED, 120 CLEANING_UP 121 }; 122 std::atomic<Sandbox_Status> sandbox_created = Sandbox_Status::NOT_CREATED; 123 124 std::mutex callback_lock; 125 std::vector<void*> callback_keys; 126 127 template<typename T> 128 using convert_fn_ptr_to_sandbox_equivalent_t = decltype( 129 ::rlbox::convert_fn_ptr_to_sandbox_equivalent_detail::helper<T_Sbx>( 130 std::declval<T>())); 131 132 template<typename T> check_invoke_param_type_is_ok()133 inline constexpr void check_invoke_param_type_is_ok() 134 { 135 using T_NoRef = std::remove_reference_t<T>; 136 137 if_constexpr_named(cond1, detail::rlbox_is_wrapper_v<T_NoRef>) 138 { 139 if_constexpr_named( 140 subcond1, 141 !std::is_same_v<T_Sbx, detail::rlbox_get_wrapper_sandbox_t<T_NoRef>>) 142 { 143 rlbox_detail_static_fail_because( 144 cond1 && subcond1, 145 "Mixing tainted data from a different sandbox types. This could " 146 "happen due to couple of different reasons.\n" 147 "1. You are using 2 sandbox types for example'rlbox_noop_sandbox' " 148 "and 'rlbox_lucet_sandbox', and are passing tainted data from one " 149 "sandbox as parameters into a function call to the other sandbox. " 150 "This is not allowed, unwrap the tainted data with copy_and_verify " 151 "or other unwrapping APIs first.\n" 152 "2. You have inadvertantly forgotten to set/remove " 153 "RLBOX_USE_STATIC_CALLS depending on the sandbox type. Some sandbox " 154 "types like rlbox_noop_sandbox require this to be set to a given " 155 "value, while other types like rlbox_lucet_sandbox, require this not " 156 "to be set."); 157 } 158 } 159 else if_constexpr_named(cond2, 160 std::is_null_pointer_v<T_NoRef> || 161 detail::is_fundamental_or_enum_v<T_NoRef>) 162 {} 163 else 164 { 165 constexpr auto unknownCase = !(cond1 || cond2); 166 rlbox_detail_static_fail_because( 167 unknownCase, 168 "Arguments to a sandbox function call should be primitives or wrapped " 169 "types like tainted, callbacks etc."); 170 } 171 } 172 173 template<typename T> invoke_process_param(T && param)174 inline auto invoke_process_param(T&& param) 175 { 176 check_invoke_param_type_is_ok<T>(); 177 178 using T_NoRef = std::remove_reference_t<T>; 179 180 if constexpr (detail::rlbox_is_tainted_opaque_v<T_NoRef>) { 181 auto ret = from_opaque(param); 182 return ret.UNSAFE_sandboxed(*this); 183 } else if constexpr (detail::rlbox_is_wrapper_v<T_NoRef>) { 184 return param.UNSAFE_sandboxed(*this); 185 } else if constexpr (std::is_null_pointer_v<T_NoRef>) { 186 tainted<void*, T_Sbx> ret = nullptr; 187 return ret.UNSAFE_sandboxed(*this); 188 } else if constexpr (detail::is_fundamental_or_enum_v<T_NoRef>) { 189 // For unwrapped primitives, assign to a tainted var and then unwrap so 190 // that we adjust for machine model 191 tainted<T_NoRef, T_Sbx> ret = param; 192 return ret.UNSAFE_sandboxed(*this); 193 } else { 194 rlbox_detail_static_fail_because(detail::true_v<T_NoRef>, "Unknown case"); 195 } 196 } 197 198 template<typename T, typename T_Arg> sandbox_callback_intercept_convert_param(rlbox_sandbox<T_Sbx> & sandbox,const T_Arg & arg)199 inline tainted<T, T_Sbx> sandbox_callback_intercept_convert_param( 200 rlbox_sandbox<T_Sbx>& sandbox, 201 const T_Arg& arg) 202 { 203 tainted<T, T_Sbx> ret; 204 using namespace detail; 205 convert_type<T_Sbx, 206 adjust_type_direction::TO_APPLICATION, 207 adjust_type_context::SANDBOX>( 208 ret.get_raw_value_ref(), 209 arg, 210 nullptr /* example_unsandboxed_ptr */, 211 &sandbox); 212 return ret; 213 } 214 215 template<typename T_Ret, typename... T_Args> 216 static detail::convert_to_sandbox_equivalent_t<T_Ret, T_Sbx> sandbox_callback_interceptor(detail::convert_to_sandbox_equivalent_t<T_Args,T_Sbx>...args)217 sandbox_callback_interceptor( 218 detail::convert_to_sandbox_equivalent_t<T_Args, T_Sbx>... args) 219 { 220 std::pair<T_Sbx*, void*> context = 221 T_Sbx::impl_get_executed_callback_sandbox_and_key(); 222 auto& sandbox = *(reinterpret_cast<rlbox_sandbox<T_Sbx>*>(context.first)); 223 auto key = context.second; 224 225 using T_Func_Ret = 226 std::conditional_t<std::is_void_v<T_Ret>, void, tainted<T_Ret, T_Sbx>>; 227 using T_Func = 228 T_Func_Ret (*)(rlbox_sandbox<T_Sbx>&, tainted<T_Args, T_Sbx>...); 229 auto target_fn_ptr = reinterpret_cast<T_Func>(key); 230 231 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 232 high_resolution_clock::time_point enter_time = high_resolution_clock::now(); 233 auto on_exit = rlbox::detail::make_scope_exit([&] { 234 auto exit_time = high_resolution_clock::now(); 235 int64_t ns = duration_cast<nanoseconds>(exit_time - enter_time).count(); 236 sandbox.transition_times.push_back( 237 rlbox_transition_timing{ rlbox_transition::CALLBACK, 238 nullptr /* func_name */, 239 key /* func_ptr */, 240 ns }); 241 }); 242 #endif 243 244 if constexpr (std::is_void_v<T_Func_Ret>) { 245 (*target_fn_ptr)( 246 sandbox, 247 sandbox.template sandbox_callback_intercept_convert_param<T_Args>( 248 sandbox, args)...); 249 return; 250 } else { 251 auto tainted_ret = (*target_fn_ptr)( 252 sandbox, 253 sandbox.template sandbox_callback_intercept_convert_param<T_Args>( 254 sandbox, args)...); 255 256 using namespace detail; 257 convert_to_sandbox_equivalent_t<T_Ret, T_Sbx> ret; 258 convert_type<T_Sbx, 259 adjust_type_direction::TO_SANDBOX, 260 adjust_type_context::SANDBOX>( 261 ret, 262 tainted_ret.get_raw_value_ref(), 263 nullptr /* example_unsandboxed_ptr */, 264 &sandbox); 265 return ret; 266 } 267 } 268 269 /** 270 * @brief Unregister a callback function and disallow the sandbox from 271 * calling this function henceforth. 272 */ 273 template<typename T_Ret, typename... T_Args> unregister_callback(void * key)274 inline void unregister_callback(void* key) 275 { 276 // Silently swallowing the failure is better here as RAII types may try to 277 // cleanup callbacks after sandbox destruction 278 if (sandbox_created.load() != Sandbox_Status::CREATED) { 279 return; 280 } 281 282 this->template impl_unregister_callback< 283 detail::convert_to_sandbox_equivalent_t<T_Ret, T_Sbx>, 284 detail::convert_to_sandbox_equivalent_t<T_Args, T_Sbx>...>(key); 285 286 std::lock_guard<std::mutex> lock(callback_lock); 287 auto el_ref = std::find(callback_keys.begin(), callback_keys.end(), key); 288 detail::dynamic_check( 289 el_ref != callback_keys.end(), 290 "Unexpected state. Unregistering a callback that was never registered."); 291 callback_keys.erase(el_ref); 292 } 293 find_sandbox_from_example(const void * example_sandbox_ptr)294 static T_Sbx* find_sandbox_from_example(const void* example_sandbox_ptr) 295 { 296 detail::dynamic_check( 297 example_sandbox_ptr != nullptr, 298 "Internal error: received a null example pointer. Please file a bug."); 299 300 RLBOX_ACQUIRE_SHARED_GUARD(lock, sandbox_list_lock); 301 for (auto sandbox_v : sandbox_list) { 302 auto sandbox = reinterpret_cast<rlbox_sandbox<T_Sbx>*>(sandbox_v); 303 if (sandbox->is_pointer_in_sandbox_memory(example_sandbox_ptr)) { 304 return sandbox; 305 } 306 } 307 308 detail::dynamic_check( 309 false, 310 "Internal error: Could not find the sandbox associated with example " 311 "pointer. Please file a bug."); 312 return nullptr; 313 } 314 315 public: 316 /***** Function to adjust for custom machine models *****/ 317 318 template<typename T> 319 using convert_to_sandbox_equivalent_nonclass_t = 320 detail::convert_base_types_t<T, 321 typename T_Sbx::T_ShortType, 322 typename T_Sbx::T_IntType, 323 typename T_Sbx::T_LongType, 324 typename T_Sbx::T_LongLongType, 325 typename T_Sbx::T_PointerType>; 326 get_sandbox_impl()327 T_Sbx* get_sandbox_impl() { return this; } 328 329 /** 330 * @brief Create a new sandbox. 331 * 332 * @tparam T_args Arguments passed to the underlying sandbox 333 * implementation. For the null sandbox, no arguments are necessary. 334 */ 335 template<typename... T_Args> create_sandbox(T_Args...args)336 inline auto create_sandbox(T_Args... args) 337 { 338 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 339 // Warm up the timer. The first call is always slow (at least on the test 340 // platform) 341 for (int i = 0; i < 10; i++) { 342 auto val = high_resolution_clock::now(); 343 RLBOX_UNUSED(val); 344 } 345 #endif 346 auto expected = Sandbox_Status::NOT_CREATED; 347 bool success = sandbox_created.compare_exchange_strong( 348 expected, Sandbox_Status::INITIALIZING /* desired */); 349 detail::dynamic_check( 350 success, 351 "create_sandbox called when sandbox already created/is being " 352 "created concurrently"); 353 354 return detail::return_first_result( 355 [&]() { 356 return this->impl_create_sandbox(std::forward<T_Args>(args)...); 357 }, 358 [&]() { 359 sandbox_created.store(Sandbox_Status::CREATED); 360 RLBOX_ACQUIRE_UNIQUE_GUARD(lock, sandbox_list_lock); 361 sandbox_list.push_back(this); 362 }); 363 } 364 365 /** 366 * @brief Destroy sandbox and reclaim any memory. 367 */ destroy_sandbox()368 inline auto destroy_sandbox() 369 { 370 auto expected = Sandbox_Status::CREATED; 371 bool success = sandbox_created.compare_exchange_strong( 372 expected, Sandbox_Status::CLEANING_UP /* desired */); 373 374 detail::dynamic_check( 375 success, 376 "destroy_sandbox called without sandbox creation/is being " 377 "destroyed concurrently"); 378 379 { 380 RLBOX_ACQUIRE_UNIQUE_GUARD(lock, sandbox_list_lock); 381 auto el_ref = std::find(sandbox_list.begin(), sandbox_list.end(), this); 382 detail::dynamic_check( 383 el_ref != sandbox_list.end(), 384 "Unexpected state. Destroying a sandbox that was never initialized."); 385 sandbox_list.erase(el_ref); 386 } 387 388 sandbox_created.store(Sandbox_Status::NOT_CREATED); 389 return this->impl_destroy_sandbox(); 390 } 391 392 template<typename T> get_unsandboxed_pointer(convert_to_sandbox_equivalent_nonclass_t<T> p) const393 inline T get_unsandboxed_pointer( 394 convert_to_sandbox_equivalent_nonclass_t<T> p) const 395 { 396 static_assert(std::is_pointer_v<T>); 397 if (p == 0) { 398 return nullptr; 399 } 400 auto ret = this->template impl_get_unsandboxed_pointer<T>(p); 401 return reinterpret_cast<T>(ret); 402 } 403 404 template<typename T> get_sandboxed_pointer(const void * p) const405 inline convert_to_sandbox_equivalent_nonclass_t<T> get_sandboxed_pointer( 406 const void* p) const 407 { 408 static_assert(std::is_pointer_v<T>); 409 if (p == nullptr) { 410 return 0; 411 } 412 return this->template impl_get_sandboxed_pointer<T>(p); 413 } 414 415 template<typename T> get_unsandboxed_pointer_no_ctx(convert_to_sandbox_equivalent_nonclass_t<T> p,const void * example_unsandboxed_ptr)416 static inline T get_unsandboxed_pointer_no_ctx( 417 convert_to_sandbox_equivalent_nonclass_t<T> p, 418 const void* example_unsandboxed_ptr) 419 { 420 static_assert(std::is_pointer_v<T>); 421 if (p == 0) { 422 return nullptr; 423 } 424 auto ret = T_Sbx::template impl_get_unsandboxed_pointer_no_ctx<T>( 425 p, example_unsandboxed_ptr, find_sandbox_from_example); 426 return reinterpret_cast<T>(ret); 427 } 428 429 template<typename T> 430 static inline convert_to_sandbox_equivalent_nonclass_t<T> get_sandboxed_pointer_no_ctx(const void * p,const void * example_unsandboxed_ptr)431 get_sandboxed_pointer_no_ctx(const void* p, 432 const void* example_unsandboxed_ptr) 433 { 434 static_assert(std::is_pointer_v<T>); 435 if (p == nullptr) { 436 return 0; 437 } 438 return T_Sbx::template impl_get_sandboxed_pointer_no_ctx<T>( 439 p, example_unsandboxed_ptr, find_sandbox_from_example); 440 } 441 442 /** 443 * @brief Allocate a new pointer that is accessible to both the application 444 * and sandbox. The pointer is allocated in sandbox memory. 445 * 446 * @tparam T The type of the pointer you want to create. If T=int, this 447 * would return a pointer to an int. 448 * 449 * @return tainted<T*, T_Sbx> Tainted pointer accessible to the application 450 * and sandbox. 451 */ 452 template<typename T> malloc_in_sandbox()453 inline tainted<T*, T_Sbx> malloc_in_sandbox() 454 { 455 const uint32_t defaultCount = 1; 456 return malloc_in_sandbox<T>(defaultCount); 457 } 458 459 /** 460 * @brief Allocate an array that is accessible to both the application 461 * and sandbox. The pointer is allocated in sandbox memory. 462 * 463 * @tparam T The type of the array elements you want to create. If T=int, this 464 * would return a pointer to an array of ints. 465 * 466 * @param count The number of array elements to allocate. 467 * 468 * @return tainted<T*, T_Sbx> Tainted pointer accessible to the application 469 * and sandbox. 470 */ 471 template<typename T> malloc_in_sandbox(uint32_t count)472 inline tainted<T*, T_Sbx> malloc_in_sandbox(uint32_t count) 473 { 474 // Silently swallowing the failure is better here as RAII types may try to 475 // malloc after sandbox destruction 476 if (sandbox_created.load() != Sandbox_Status::CREATED) { 477 return tainted<T*, T_Sbx>::internal_factory(nullptr); 478 } 479 480 detail::dynamic_check(count != 0, "Malloc tried to allocate 0 bytes"); 481 auto ptr_in_sandbox = this->impl_malloc_in_sandbox(sizeof(T) * count); 482 auto ptr = get_unsandboxed_pointer<T*>(ptr_in_sandbox); 483 if (!ptr) { 484 return tainted<T*, T_Sbx>(nullptr); 485 } 486 detail::dynamic_check(is_pointer_in_sandbox_memory(ptr), 487 "Malloc returned pointer outside the sandbox memory"); 488 auto ptr_end = reinterpret_cast<uintptr_t>(ptr + (count - 1)); 489 detail::dynamic_check( 490 is_in_same_sandbox(ptr, reinterpret_cast<void*>(ptr_end)), 491 "Malloc returned a pointer whose range goes beyond sandbox memory"); 492 auto cast_ptr = reinterpret_cast<T*>(ptr); 493 return tainted<T*, T_Sbx>::internal_factory(cast_ptr); 494 } 495 496 /** 497 * @brief Free the memory referenced by the tainted pointer. 498 * 499 * @param ptr Pointer to sandbox memory to free. 500 */ 501 template<typename T> free_in_sandbox(tainted<T *,T_Sbx> ptr)502 inline void free_in_sandbox(tainted<T*, T_Sbx> ptr) 503 { 504 // Silently swallowing the failure is better here as RAII types may try to 505 // free after sandbox destruction 506 if (sandbox_created.load() != Sandbox_Status::CREATED) { 507 return; 508 } 509 510 this->impl_free_in_sandbox(ptr.get_raw_sandbox_value(*this)); 511 } 512 513 /** 514 * @brief Check if two pointers are in the same sandbox. 515 * For the null-sandbox, this always returns true. 516 */ is_in_same_sandbox(const void * p1,const void * p2)517 static inline bool is_in_same_sandbox(const void* p1, const void* p2) 518 { 519 return T_Sbx::impl_is_in_same_sandbox(p1, p2); 520 } 521 522 /** 523 * @brief Check if the pointer points to this sandbox's memory. 524 * For the null-sandbox, this always returns true. 525 */ is_pointer_in_sandbox_memory(const void * p)526 inline bool is_pointer_in_sandbox_memory(const void* p) 527 { 528 return this->impl_is_pointer_in_sandbox_memory(p); 529 } 530 531 /** 532 * @brief Check if the pointer points to application memory. 533 * For the null-sandbox, this always returns true. 534 */ is_pointer_in_app_memory(const void * p)535 inline bool is_pointer_in_app_memory(const void* p) 536 { 537 return this->impl_is_pointer_in_app_memory(p); 538 } 539 get_total_memory()540 inline size_t get_total_memory() { return this->impl_get_total_memory(); } 541 get_memory_location()542 inline void* get_memory_location() 543 { 544 return this->impl_get_memory_location(); 545 } 546 lookup_symbol(const char * func_name)547 void* lookup_symbol(const char* func_name) 548 { 549 { 550 RLBOX_ACQUIRE_SHARED_GUARD(lock, func_ptr_cache_lock); 551 552 auto func_ptr_ref = func_ptr_map.find(func_name); 553 if (func_ptr_ref != func_ptr_map.end()) { 554 return func_ptr_ref->second; 555 } 556 } 557 558 void* func_ptr = this->impl_lookup_symbol(func_name); 559 RLBOX_ACQUIRE_UNIQUE_GUARD(lock, func_ptr_cache_lock); 560 func_ptr_map[func_name] = func_ptr; 561 return func_ptr; 562 } 563 564 // this is an internal function invoked from macros, so it has be public 565 template<typename T, typename... T_Args> INTERNAL_invoke_with_func_name(const char * func_name,T_Args &&...params)566 inline auto INTERNAL_invoke_with_func_name(const char* func_name, 567 T_Args&&... params) 568 { 569 return INTERNAL_invoke_with_func_ptr<T, T_Args...>( 570 func_name, lookup_symbol(func_name), std::forward<T_Args>(params)...); 571 } 572 573 // this is an internal function invoked from macros, so it has be public 574 // Explicitly don't use inline on this, as this adds a lot of instructions 575 // prior to function call. What's more, by not inlining, different function 576 // calls with the same signature can share the same code segments for 577 // sandboxed function execution in the binary 578 template<typename T, typename... T_Args> INTERNAL_invoke_with_func_ptr(const char * func_name,void * func_ptr,T_Args &&...params)579 auto INTERNAL_invoke_with_func_ptr(const char* func_name, 580 void* func_ptr, 581 T_Args&&... params) 582 { 583 // unused in some paths 584 RLBOX_UNUSED(func_name); 585 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 586 auto enter_time = high_resolution_clock::now(); 587 auto on_exit = rlbox::detail::make_scope_exit([&] { 588 auto exit_time = high_resolution_clock::now(); 589 int64_t ns = duration_cast<nanoseconds>(exit_time - enter_time).count(); 590 transition_times.push_back(rlbox_transition_timing{ 591 rlbox_transition::INVOKE, func_name, func_ptr, ns }); 592 }); 593 #endif 594 (check_invoke_param_type_is_ok<T_Args>(), ...); 595 596 static_assert( 597 rlbox::detail::polyfill::is_invocable_v< 598 T, 599 detail::rlbox_remove_wrapper_t<std::remove_reference_t<T_Args>>...>, 600 "Mismatched arguments types for function"); 601 602 using T_Result = rlbox::detail::polyfill::invoke_result_t< 603 T, 604 detail::rlbox_remove_wrapper_t<std::remove_reference_t<T_Args>>...>; 605 606 using T_Converted = 607 std::remove_pointer_t<convert_fn_ptr_to_sandbox_equivalent_t<T*>>; 608 609 if constexpr (std::is_void_v<T_Result>) { 610 this->template impl_invoke_with_func_ptr<T>( 611 reinterpret_cast<T_Converted*>(func_ptr), 612 invoke_process_param(params)...); 613 return; 614 } else { 615 auto raw_result = this->template impl_invoke_with_func_ptr<T>( 616 reinterpret_cast<T_Converted*>(func_ptr), 617 invoke_process_param(params)...); 618 tainted<T_Result, T_Sbx> wrapped_result; 619 using namespace detail; 620 convert_type<T_Sbx, 621 adjust_type_direction::TO_APPLICATION, 622 adjust_type_context::SANDBOX>( 623 wrapped_result.get_raw_value_ref(), 624 raw_result, 625 nullptr /* example_unsandboxed_ptr */, 626 this /* sandbox_ptr */); 627 return wrapped_result; 628 } 629 } 630 631 // Useful in the porting stage to temporarily allow non tainted pointers to go 632 // through. This will only ever work in the rlbox_noop_sandbox. Any sandbox 633 // that actually enforces isolation will crash here. 634 template<typename T2> UNSAFE_accept_pointer(T2 ptr)635 tainted<T2, T_Sbx> UNSAFE_accept_pointer(T2 ptr) 636 { 637 static_assert(std::is_pointer_v<T2>, 638 "UNSAFE_accept_pointer expects a pointer param"); 639 tainted<T2, T_Sbx> ret; 640 ret.assign_raw_pointer(*this, ptr); 641 return ret; 642 } 643 644 template<typename T_Ret, typename... T_Args> 645 using T_Cb_no_wrap = detail::rlbox_remove_wrapper_t<T_Ret>( 646 detail::rlbox_remove_wrapper_t<T_Args>...); 647 648 template<typename T_Ret> register_callback(T_Ret (*)())649 sandbox_callback<T_Cb_no_wrap<T_Ret>*, T_Sbx> register_callback(T_Ret (*)()) 650 { 651 rlbox_detail_static_fail_because( 652 detail::true_v<T_Ret>, 653 "Modify the callback to change the first parameter to a sandbox." 654 "For instance if a callback has type\n\n" 655 "int foo() {...}\n\n" 656 "Change this to \n\n" 657 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox) {...}\n"); 658 659 // this is never executed, but we need it for the function to type-check 660 std::abort(); 661 } 662 663 /** 664 * @brief Expose a callback function to the sandboxed code. 665 * 666 * @param func_ptr The callback to expose. 667 * 668 * @tparam T_RL Sandbox reference type (first argument). 669 * @tparam T_Ret Return type of callback. Must be tainted or void. 670 * @tparam T_Args Types of remaining callback arguments. Must be tainted. 671 * 672 * @return Wrapped callback function pointer that can be passed to the 673 * sandbox. 674 */ 675 template<typename T_RL, typename T_Ret, typename... T_Args> register_callback(T_Ret (* func_ptr)(T_RL,T_Args...))676 sandbox_callback<T_Cb_no_wrap<T_Ret, T_Args...>*, T_Sbx> register_callback( 677 T_Ret (*func_ptr)(T_RL, T_Args...)) 678 { 679 // Some branches don't use the param 680 RLBOX_UNUSED(func_ptr); 681 682 if_constexpr_named(cond1, !std::is_same_v<T_RL, rlbox_sandbox<T_Sbx>&>) 683 { 684 rlbox_detail_static_fail_because( 685 cond1, 686 "Modify the callback to change the first parameter to a sandbox." 687 "For instance if a callback has type\n\n" 688 "int foo(int a, int b) {...}\n\n" 689 "Change this to \n\n" 690 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox," 691 "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n"); 692 } 693 else if_constexpr_named( 694 cond2, !(detail::rlbox_is_tainted_or_opaque_v<T_Args> && ...)) 695 { 696 rlbox_detail_static_fail_because( 697 cond2, 698 "Change all arguments to the callback have to be tainted or " 699 "tainted_opaque." 700 "For instance if a callback has type\n\n" 701 "int foo(int a, int b) {...}\n\n" 702 "Change this to \n\n" 703 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox," 704 "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n"); 705 } 706 else if_constexpr_named( 707 cond3, (std::is_array_v<detail::rlbox_remove_wrapper_t<T_Args>> || ...)) 708 { 709 rlbox_detail_static_fail_because( 710 cond3, 711 "Change all static array arguments to the callback to be pointers." 712 "For instance if a callback has type\n\n" 713 "int foo(int a[4]) {...}\n\n" 714 "Change this to \n\n" 715 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox," 716 "tainted<int*, T_Sbx> a) {...}\n"); 717 } 718 else if_constexpr_named( 719 cond4, 720 !(std::is_void_v<T_Ret> || detail::rlbox_is_tainted_or_opaque_v<T_Ret>)) 721 { 722 rlbox_detail_static_fail_because( 723 cond4, 724 "Change the callback return type to be tainted or tainted_opaque if it " 725 "is not void." 726 "For instance if a callback has type\n\n" 727 "int foo(int a, int b) {...}\n\n" 728 "Change this to \n\n" 729 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox," 730 "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n"); 731 } 732 else 733 { 734 detail::dynamic_check( 735 sandbox_created.load() == Sandbox_Status::CREATED, 736 "register_callback called without sandbox creation"); 737 738 // Need unique key for each callback we register - just use the func addr 739 void* unique_key = reinterpret_cast<void*>(func_ptr); 740 741 // Make sure that the user hasn't previously registered this function... 742 // If they have, we would returning 2 owning types (sandbox_callback) to 743 // the same callback which would be bad 744 { 745 std::lock_guard<std::mutex> lock(callback_lock); 746 bool exists = 747 std::find(callback_keys.begin(), callback_keys.end(), unique_key) != 748 callback_keys.end(); 749 detail::dynamic_check( 750 !exists, "You have previously already registered this callback."); 751 callback_keys.push_back(unique_key); 752 } 753 754 auto callback_interceptor = 755 sandbox_callback_interceptor<detail::rlbox_remove_wrapper_t<T_Ret>, 756 detail::rlbox_remove_wrapper_t<T_Args>...>; 757 758 auto callback_trampoline = this->template impl_register_callback< 759 detail::convert_to_sandbox_equivalent_t< 760 detail::rlbox_remove_wrapper_t<T_Ret>, 761 T_Sbx>, 762 detail::convert_to_sandbox_equivalent_t< 763 detail::rlbox_remove_wrapper_t<T_Args>, 764 T_Sbx>...>(unique_key, reinterpret_cast<void*>(callback_interceptor)); 765 766 auto tainted_func_ptr = reinterpret_cast< 767 detail::rlbox_tainted_opaque_to_tainted_t<T_Ret, T_Sbx> (*)( 768 T_RL, detail::rlbox_tainted_opaque_to_tainted_t<T_Args, T_Sbx>...)>( 769 reinterpret_cast<void*>(func_ptr)); 770 771 auto ret = sandbox_callback<T_Cb_no_wrap<T_Ret, T_Args...>*, T_Sbx>( 772 this, 773 tainted_func_ptr, 774 callback_interceptor, 775 callback_trampoline, 776 unique_key); 777 return ret; 778 } 779 } 780 781 // this is an internal function invoked from macros, so it has be public 782 template<typename T> INTERNAL_get_sandbox_function_name(const char * func_name)783 inline tainted<T*, T_Sbx> INTERNAL_get_sandbox_function_name( 784 const char* func_name) 785 { 786 return INTERNAL_get_sandbox_function_ptr<T>(lookup_symbol(func_name)); 787 } 788 789 // this is an internal function invoked from macros, so it has be public 790 template<typename T> INTERNAL_get_sandbox_function_ptr(void * func_ptr)791 inline tainted<T*, T_Sbx> INTERNAL_get_sandbox_function_ptr(void* func_ptr) 792 { 793 return tainted<T*, T_Sbx>::internal_factory(reinterpret_cast<T*>(func_ptr)); 794 } 795 796 #ifdef RLBOX_MEASURE_TRANSITION_TIMES 797 inline std::vector<rlbox_transition_timing>& process_and_get_transition_times()798 process_and_get_transition_times() 799 { 800 return transition_times; 801 } 802 #endif 803 }; 804 805 #if defined(__clang__) 806 # pragma clang diagnostic push 807 # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 808 #elif defined(__GNUC__) || defined(__GNUG__) 809 // Can't turn off the variadic macro warning emitted from -pedantic so use a 810 // hack to stop GCC emitting warnings for the reminder of this file 811 # pragma GCC system_header 812 #elif defined(_MSC_VER) 813 // Doesn't seem to emit the warning 814 #else 815 // Don't know the compiler... just let it go through 816 #endif 817 818 /** 819 * @def invoke_sandbox_function 820 * @brief Call sandbox function. 821 * 822 * @param func_name The sandboxed library function to call. 823 * @param ... Arguments to function should be simple or tainted values. 824 * @return Tainted value or void. 825 */ 826 #ifdef RLBOX_USE_STATIC_CALLS 827 828 # define sandbox_lookup_symbol_helper(prefix, func_name) prefix(func_name) 829 830 # define invoke_sandbox_function(func_name, ...) \ 831 template INTERNAL_invoke_with_func_ptr<decltype(func_name)>( \ 832 #func_name, \ 833 sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name), \ 834 ##__VA_ARGS__) 835 836 # define get_sandbox_function_address(func_name) \ 837 template INTERNAL_get_sandbox_function_ptr<decltype(func_name)>( \ 838 sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name)) 839 840 #else 841 842 # define invoke_sandbox_function(func_name, ...) \ 843 template INTERNAL_invoke_with_func_name<decltype(func_name)>( \ 844 #func_name, ##__VA_ARGS__) 845 846 # define get_sandbox_function_address(func_name) \ 847 template INTERNAL_get_sandbox_function_name<decltype(func_name)>(#func_name) 848 849 #endif 850 851 #define sandbox_invoke(sandbox, func_name, ...) \ 852 (sandbox).invoke_sandbox_function(func_name, ##__VA_ARGS__) 853 854 #define sandbox_function_address(sandbox, func_name) \ 855 (sandbox).get_sandbox_function_address(func_name) 856 857 #if defined(__clang__) 858 # pragma clang diagnostic pop 859 #else 860 #endif 861 862 } 863