#pragma once // IWYU pragma: private, include "rlbox.hpp" // IWYU pragma: friend "rlbox_.*\.hpp" #include #include #ifdef RLBOX_MEASURE_TRANSITION_TIMES # include #endif #include #include #include #include #ifndef RLBOX_USE_CUSTOM_SHARED_LOCK # include #endif #ifdef RLBOX_MEASURE_TRANSITION_TIMES # include # include #endif #include #include #include #include #include "rlbox_conversion.hpp" #include "rlbox_helpers.hpp" #include "rlbox_stdlib_polyfill.hpp" #include "rlbox_struct_support.hpp" #include "rlbox_type_traits.hpp" #include "rlbox_wrapper_traits.hpp" #ifdef RLBOX_MEASURE_TRANSITION_TIMES using namespace std::chrono; #endif namespace rlbox { namespace convert_fn_ptr_to_sandbox_equivalent_detail { template using conv = ::rlbox::detail::convert_to_sandbox_equivalent_t; template using T_Func = T_Ret (*)(T_Args...); template T_Func, conv...> helper( T_Ret (*)(T_Args...)); } #if defined(RLBOX_MEASURE_TRANSITION_TIMES) || \ defined(RLBOX_TRANSITION_ACTION_OUT) || defined(RLBOX_TRANSITION_ACTION_IN) enum class rlbox_transition { INVOKE, CALLBACK }; #endif #ifdef RLBOX_MEASURE_TRANSITION_TIMES struct rlbox_transition_timing { rlbox_transition invoke; const char* name; void* ptr; int64_t time; std::string to_string() { std::ostringstream ret; if (invoke == rlbox_transition::INVOKE) { ret << name; } else { ret << "Callback " << ptr; } ret << " : " << time << "\n"; return ret.str(); } }; #endif #ifndef RLBOX_SINGLE_THREADED_INVOCATIONS # error \ "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." #endif /** * @brief Encapsulation for sandboxes. * * @tparam T_Sbx Type of sandbox. For the null sandbox this is * `rlbox_noop_sandbox` */ template class rlbox_sandbox : protected T_Sbx { KEEP_CLASSES_FRIENDLY private: #ifdef RLBOX_MEASURE_TRANSITION_TIMES std::vector transition_times; #endif static inline RLBOX_SHARED_LOCK(sandbox_list_lock); // The actual type of the vector is std::vector*> // However clang 5, 6 have bugs where compilation seg-faults on this type // So we just use this std::vector static inline std::vector sandbox_list; RLBOX_SHARED_LOCK(func_ptr_cache_lock); std::map func_ptr_map; app_pointer_map app_ptr_map; // This variable tracks of the sandbox has already been created/destroyed. // APIs in this class should be called only when the sandbox is created. // However, it is expensive to check in APIs such as invoke or in the callback // interceptor. What's more, there could be time of check time of use issues // in the checks as well. // In general, we leave it up to the user to ensure these APIs are never // called prior to sandbox construction or after destruction. We perform some // conservative sanity checks, where they would not add too much overhead. enum class Sandbox_Status { NOT_CREATED, INITIALIZING, CREATED, CLEANING_UP }; std::atomic sandbox_created = Sandbox_Status::NOT_CREATED; std::mutex callback_lock; std::vector callback_keys; void* transition_state = nullptr; template using convert_fn_ptr_to_sandbox_equivalent_t = decltype(::rlbox::convert_fn_ptr_to_sandbox_equivalent_detail::helper< T_Sbx>(std::declval())); template inline constexpr void check_invoke_param_type_is_ok() { using T_NoRef = std::remove_reference_t; if_constexpr_named(cond1, detail::rlbox_is_wrapper_v) { if_constexpr_named( subcond1, !std::is_same_v>) { rlbox_detail_static_fail_because( cond1 && subcond1, "Mixing tainted data from a different sandbox types. This could " "happen due to couple of different reasons.\n" "1. You are using 2 sandbox types for example'rlbox_noop_sandbox' " "and 'rlbox_lucet_sandbox', and are passing tainted data from one " "sandbox as parameters into a function call to the other sandbox. " "This is not allowed, unwrap the tainted data with copy_and_verify " "or other unwrapping APIs first.\n" "2. You have inadvertantly forgotten to set/remove " "RLBOX_USE_STATIC_CALLS depending on the sandbox type. Some sandbox " "types like rlbox_noop_sandbox require this to be set to a given " "value, while other types like rlbox_lucet_sandbox, require this not " "to be set."); } } else if_constexpr_named(cond2, std::is_null_pointer_v || detail::is_fundamental_or_enum_v) {} else { constexpr auto unknownCase = !(cond1 || cond2); rlbox_detail_static_fail_because( unknownCase, "Arguments to a sandbox function call should be primitives or wrapped " "types like tainted, callbacks etc."); } } template inline auto invoke_process_param(T&& param) { check_invoke_param_type_is_ok(); using T_NoRef = std::remove_reference_t; if constexpr (detail::rlbox_is_tainted_opaque_v) { auto ret = from_opaque(param); return ret.UNSAFE_sandboxed(*this); } else if constexpr (detail::rlbox_is_wrapper_v) { return param.UNSAFE_sandboxed(*this); } else if constexpr (std::is_null_pointer_v) { tainted ret = nullptr; return ret.UNSAFE_sandboxed(*this); } else if constexpr (detail::is_fundamental_or_enum_v) { // For unwrapped primitives, assign to a tainted var and then unwrap so // that we adjust for machine model tainted ret = param; return ret.UNSAFE_sandboxed(*this); } else { rlbox_detail_static_fail_because( detail::true_v, "Only tainted types, callbacks or primitive values such as ints can be " "passed as parameters.\n" "To make a parameter tainted, try moving the allocation into the " "sandbox.\n" "If the parameter is a callback, try registering the callback via the " "register_callback API."); } } template inline tainted sandbox_callback_intercept_convert_param( rlbox_sandbox& sandbox, const T_Arg& arg) { tainted ret; using namespace detail; convert_type( ret.get_raw_value_ref(), arg, nullptr /* example_unsandboxed_ptr */, &sandbox); return ret; } template static detail::convert_to_sandbox_equivalent_t sandbox_callback_interceptor( detail::convert_to_sandbox_equivalent_t... args) { std::pair context = T_Sbx::impl_get_executed_callback_sandbox_and_key(); auto& sandbox = *(reinterpret_cast*>(context.first)); auto key = context.second; using T_Func_Ret = std::conditional_t, void, tainted>; using T_Func = T_Func_Ret (*)(rlbox_sandbox&, tainted...); auto target_fn_ptr = reinterpret_cast(key); #ifdef RLBOX_MEASURE_TRANSITION_TIMES high_resolution_clock::time_point enter_time = high_resolution_clock::now(); auto on_exit = rlbox::detail::make_scope_exit([&] { auto exit_time = high_resolution_clock::now(); int64_t ns = duration_cast(exit_time - enter_time).count(); sandbox.transition_times.push_back( rlbox_transition_timing{ rlbox_transition::CALLBACK, nullptr /* func_name */, key /* func_ptr */, ns }); }); #endif #ifdef RLBOX_TRANSITION_ACTION_OUT RLBOX_TRANSITION_ACTION_OUT(rlbox_transition::CALLBACK, nullptr /* func_name */, key /* func_ptr */, sandbox.transition_state); #endif #ifdef RLBOX_TRANSITION_ACTION_IN auto on_exit_transition = rlbox::detail::make_scope_exit([&] { RLBOX_TRANSITION_ACTION_IN(rlbox_transition::CALLBACK, nullptr /* func_name */, key /* func_ptr */, sandbox.transition_state); }); #endif if constexpr (std::is_void_v) { (*target_fn_ptr)( sandbox, sandbox.template sandbox_callback_intercept_convert_param( sandbox, args)...); return; } else { auto tainted_ret = (*target_fn_ptr)( sandbox, sandbox.template sandbox_callback_intercept_convert_param( sandbox, args)...); using namespace detail; convert_to_sandbox_equivalent_t ret; convert_type( ret, tainted_ret.get_raw_value_ref(), nullptr /* example_unsandboxed_ptr */, &sandbox); return ret; } } /** * @brief Unregister a callback function and disallow the sandbox from * calling this function henceforth. */ template inline void unregister_callback(void* key) { // Silently swallowing the failure is better here as RAII types may try to // cleanup callbacks after sandbox destruction if (sandbox_created.load() != Sandbox_Status::CREATED) { return; } this->template impl_unregister_callback< detail::convert_to_sandbox_equivalent_t, detail::convert_to_sandbox_equivalent_t...>(key); std::lock_guard lock(callback_lock); auto el_ref = std::find(callback_keys.begin(), callback_keys.end(), key); detail::dynamic_check( el_ref != callback_keys.end(), "Unexpected state. Unregistering a callback that was never registered."); callback_keys.erase(el_ref); } static T_Sbx* find_sandbox_from_example(const void* example_sandbox_ptr) { detail::dynamic_check( example_sandbox_ptr != nullptr, "Internal error: received a null example pointer. Please file a bug."); RLBOX_ACQUIRE_SHARED_GUARD(lock, sandbox_list_lock); for (auto sandbox_v : sandbox_list) { auto sandbox = reinterpret_cast*>(sandbox_v); if (sandbox->is_pointer_in_sandbox_memory(example_sandbox_ptr)) { return sandbox; } } return nullptr; } template static auto impl_create_sandbox_helper(rlbox_sandbox* this_ptr, T_Args... args) { return this_ptr->impl_create_sandbox(std::forward(args)...); } public: /** * @brief Unused member that allows the calling code to save data in a * "per-sandbox" storage. This can be useful to save context which is used * in callbacks. */ void* sandbox_storage; /***** Function to adjust for custom machine models *****/ template using convert_to_sandbox_equivalent_nonclass_t = detail::convert_base_types_t; T_Sbx* get_sandbox_impl() { return this; } /** * @brief Create a new sandbox. * * @tparam T_Args Arguments passed to the underlying sandbox * implementation. For the null sandbox, no arguments are necessary. */ template inline bool create_sandbox(T_Args... args) { #ifdef RLBOX_MEASURE_TRANSITION_TIMES // Warm up the timer. The first call is always slow (at least on the test // platform) for (int i = 0; i < 10; i++) { auto val = high_resolution_clock::now(); RLBOX_UNUSED(val); } #endif auto expected = Sandbox_Status::NOT_CREATED; bool success = sandbox_created.compare_exchange_strong( expected, Sandbox_Status::INITIALIZING /* desired */); detail::dynamic_check( success, "create_sandbox called when sandbox already created/is being " "created concurrently"); using T_Result = rlbox::detail::polyfill::invoke_result_t< decltype(impl_create_sandbox_helper), decltype(this), T_Args...>; bool created = true; if constexpr (std::is_same_v) { this->impl_create_sandbox(std::forward(args)...); } else if constexpr (std::is_same_v) { created = this->impl_create_sandbox(std::forward(args)...); } else { rlbox_detail_static_fail_because( (!std::is_same_v && !std::is_same_v), "Expected impl_create_sandbox to return void or a boolean"); } if (created) { sandbox_created.store(Sandbox_Status::CREATED); RLBOX_ACQUIRE_UNIQUE_GUARD(lock, sandbox_list_lock); sandbox_list.push_back(this); } return created; } /** * @brief Destroy sandbox and reclaim any memory. */ inline auto destroy_sandbox() { auto expected = Sandbox_Status::CREATED; bool success = sandbox_created.compare_exchange_strong( expected, Sandbox_Status::CLEANING_UP /* desired */); detail::dynamic_check( success, "destroy_sandbox called without sandbox creation/is being " "destroyed concurrently"); { RLBOX_ACQUIRE_UNIQUE_GUARD(lock, sandbox_list_lock); auto el_ref = std::find(sandbox_list.begin(), sandbox_list.end(), this); detail::dynamic_check( el_ref != sandbox_list.end(), "Unexpected state. Destroying a sandbox that was never initialized."); sandbox_list.erase(el_ref); } sandbox_created.store(Sandbox_Status::NOT_CREATED); return this->impl_destroy_sandbox(); } template inline T get_unsandboxed_pointer( convert_to_sandbox_equivalent_nonclass_t p) const { static_assert(std::is_pointer_v); if (p == 0) { return nullptr; } auto ret = this->template impl_get_unsandboxed_pointer(p); return reinterpret_cast(ret); } template inline convert_to_sandbox_equivalent_nonclass_t get_sandboxed_pointer( const void* p) const { static_assert(std::is_pointer_v); if (p == nullptr) { return 0; } return this->template impl_get_sandboxed_pointer(p); } template static inline T get_unsandboxed_pointer_no_ctx( convert_to_sandbox_equivalent_nonclass_t p, const void* example_unsandboxed_ptr) { static_assert(std::is_pointer_v); if (p == 0) { return nullptr; } auto ret = T_Sbx::template impl_get_unsandboxed_pointer_no_ctx( p, example_unsandboxed_ptr, find_sandbox_from_example); return reinterpret_cast(ret); } template static inline convert_to_sandbox_equivalent_nonclass_t get_sandboxed_pointer_no_ctx(const void* p, const void* example_unsandboxed_ptr) { static_assert(std::is_pointer_v); if (p == nullptr) { return 0; } return T_Sbx::template impl_get_sandboxed_pointer_no_ctx( p, example_unsandboxed_ptr, find_sandbox_from_example); } /** * @brief Allocate a new pointer that is accessible to both the application * and sandbox. The pointer is allocated in sandbox memory. * * @tparam T The type of the pointer you want to create. If T=int, this * would return a pointer to an int. * * @return tainted Tainted pointer accessible to the application * and sandbox. */ template inline tainted malloc_in_sandbox() { const uint32_t defaultCount = 1; return malloc_in_sandbox(defaultCount); } /** * @brief Allocate an array that is accessible to both the application * and sandbox. The pointer is allocated in sandbox memory. * * @tparam T The type of the array elements you want to create. If T=int, this * would return a pointer to an array of ints. * * @param count The number of array elements to allocate. * * @return tainted Tainted pointer accessible to the application * and sandbox. */ template inline tainted malloc_in_sandbox(uint32_t count) { // Silently swallowing the failure is better here as RAII types may try to // malloc after sandbox destruction if (sandbox_created.load() != Sandbox_Status::CREATED) { return tainted::internal_factory(nullptr); } detail::dynamic_check(count != 0, "Malloc tried to allocate 0 bytes"); if constexpr (sizeof(T) >= std::numeric_limits::max()) { rlbox_detail_static_fail_because(sizeof(T) >= std::numeric_limits::max(), "Tried to allocate an object over 4GB."); } auto total_size = static_cast(sizeof(T)) * count; if constexpr (sizeof(size_t) == 4) { // On a 32-bit platform, we need to make sure that total_size is not >=4GB detail::dynamic_check(total_size < std::numeric_limits::max(), "Tried to allocate memory over 4GB"); } else if constexpr (sizeof(size_t) != 8) { // Double check we are on a 64-bit platform // Note for static checks we need to have some dependence on T, so adding // a dummy constexpr bool dummy = sizeof(T) >= 0; rlbox_detail_static_fail_because(dummy && sizeof(size_t) != 8, "Expected 32 or 64 bit platform."); } auto ptr_in_sandbox = this->impl_malloc_in_sandbox(total_size); auto ptr = get_unsandboxed_pointer(ptr_in_sandbox); if (!ptr) { return tainted(nullptr); } detail::dynamic_check(is_pointer_in_sandbox_memory(ptr), "Malloc returned pointer outside the sandbox memory"); auto ptr_end = reinterpret_cast(ptr + (count - 1)); detail::dynamic_check( is_in_same_sandbox(ptr, reinterpret_cast(ptr_end)), "Malloc returned a pointer whose range goes beyond sandbox memory"); auto cast_ptr = reinterpret_cast(ptr); return tainted::internal_factory(cast_ptr); } /** * @brief Free the memory referenced by the tainted pointer. * * @param ptr Pointer to sandbox memory to free. */ template inline void free_in_sandbox(tainted ptr) { // Silently swallowing the failure is better here as RAII types may try to // free after sandbox destruction if (sandbox_created.load() != Sandbox_Status::CREATED) { return; } this->impl_free_in_sandbox(ptr.get_raw_sandbox_value(*this)); } /** * @brief Free the memory referenced by a tainted_volatile pointer ref. * * @param ptr_ref Pointer reference to sandbox memory to free. */ template inline void free_in_sandbox(tainted_volatile& ptr_ref) { tainted ptr = ptr_ref; free_in_sandbox(ptr); } /** * @brief Free the memory referenced by a tainted_opaque pointer. * * @param ptr_opaque Opaque pointer to sandbox memory to free. */ template inline void free_in_sandbox(tainted_opaque ptr_opaque) { tainted ptr = from_opaque(ptr_opaque); free_in_sandbox(ptr); } /** * @brief Check if two pointers are in the same sandbox. * For the null-sandbox, this always returns true. */ static inline bool is_in_same_sandbox(const void* p1, const void* p2) { const size_t num_args = detail::func_arg_nums_v; if constexpr (num_args == 2) { return T_Sbx::impl_is_in_same_sandbox(p1, p2); } else { return T_Sbx::impl_is_in_same_sandbox(p1, p2, find_sandbox_from_example); } } /** * @brief Check if the pointer points to this sandbox's memory. * For the null-sandbox, this always returns true. */ inline bool is_pointer_in_sandbox_memory(const void* p) { return this->impl_is_pointer_in_sandbox_memory(p); } /** * @brief Check if the pointer points to application memory. * For the null-sandbox, this always returns true. */ inline bool is_pointer_in_app_memory(const void* p) { return this->impl_is_pointer_in_app_memory(p); } inline size_t get_total_memory() { return this->impl_get_total_memory(); } inline void* get_memory_location() { return this->impl_get_memory_location(); } void* get_transition_state() { return transition_state; } void set_transition_state(void* new_state) { transition_state = new_state; } /** * @brief For internal use only. * Grant access of the passed in buffer in to the sandbox instance. Called by * internal APIs only if the underlying sandbox supports * can_grant_deny_access by including the line * ``` * using can_grant_deny_access = void; * ``` */ template inline tainted INTERNAL_grant_access(T* src, size_t num, bool& success) { auto ret = this->impl_grant_access(src, num, success); return tainted::internal_factory(ret); } /** * @brief For internal use only. * Grant access of the passed in buffer in to the sandbox instance. Called by * internal APIs only if the underlying sandbox supports * can_grant_deny_access by including the line * ``` * using can_grant_deny_access = void; * ``` */ template inline T* INTERNAL_deny_access(tainted src, size_t num, bool& success) { auto ret = this->impl_deny_access(src.INTERNAL_unverified_safe(), num, success); return ret; } void* lookup_symbol(const char* func_name) { { RLBOX_ACQUIRE_SHARED_GUARD(lock, func_ptr_cache_lock); auto func_ptr_ref = func_ptr_map.find(func_name); if (func_ptr_ref != func_ptr_map.end()) { return func_ptr_ref->second; } } void* func_ptr = this->impl_lookup_symbol(func_name); RLBOX_ACQUIRE_UNIQUE_GUARD(lock, func_ptr_cache_lock); func_ptr_map[func_name] = func_ptr; return func_ptr; } void* internal_lookup_symbol(const char* func_name) { { RLBOX_ACQUIRE_SHARED_GUARD(lock, func_ptr_cache_lock); auto func_ptr_ref = func_ptr_map.find(func_name); if (func_ptr_ref != func_ptr_map.end()) { return func_ptr_ref->second; } } void* func_ptr = 0; if constexpr (rlbox::detail:: has_member_using_needs_internal_lookup_symbol_v) { func_ptr = this->impl_internal_lookup_symbol(func_name); } else { func_ptr = this->impl_lookup_symbol(func_name); } RLBOX_ACQUIRE_UNIQUE_GUARD(lock, func_ptr_cache_lock); func_ptr_map[func_name] = func_ptr; return func_ptr; } // this is an internal function invoked from macros, so it has be public template inline auto INTERNAL_invoke_with_func_name(const char* func_name, T_Args&&... params) { return INTERNAL_invoke_with_func_ptr( func_name, lookup_symbol(func_name), std::forward(params)...); } // this is an internal function invoked from macros, so it has be public // Explicitly don't use inline on this, as this adds a lot of instructions // prior to function call. What's more, by not inlining, different function // calls with the same signature can share the same code segments for // sandboxed function execution in the binary template auto INTERNAL_invoke_with_func_ptr(const char* func_name, void* func_ptr, T_Args&&... params) { // unused in some paths RLBOX_UNUSED(func_name); #ifdef RLBOX_MEASURE_TRANSITION_TIMES auto enter_time = high_resolution_clock::now(); auto on_exit = rlbox::detail::make_scope_exit([&] { auto exit_time = high_resolution_clock::now(); int64_t ns = duration_cast(exit_time - enter_time).count(); transition_times.push_back(rlbox_transition_timing{ rlbox_transition::INVOKE, func_name, func_ptr, ns }); }); #endif #ifdef RLBOX_TRANSITION_ACTION_IN RLBOX_TRANSITION_ACTION_IN( rlbox_transition::INVOKE, func_name, func_ptr, transition_state); #endif #ifdef RLBOX_TRANSITION_ACTION_OUT auto on_exit_transition = rlbox::detail::make_scope_exit([&] { RLBOX_TRANSITION_ACTION_OUT( rlbox_transition::INVOKE, func_name, func_ptr, transition_state); }); #endif (check_invoke_param_type_is_ok(), ...); static_assert( rlbox::detail::polyfill::is_invocable_v< T, detail::rlbox_remove_wrapper_t>...>, "Mismatched arguments types for function"); using T_Result = rlbox::detail::polyfill::invoke_result_t< T, detail::rlbox_remove_wrapper_t>...>; using T_Converted = std::remove_pointer_t>; if constexpr (std::is_void_v) { this->template impl_invoke_with_func_ptr( reinterpret_cast(func_ptr), invoke_process_param(params)...); return; } else { auto raw_result = this->template impl_invoke_with_func_ptr( reinterpret_cast(func_ptr), invoke_process_param(params)...); tainted wrapped_result; using namespace detail; convert_type( wrapped_result.get_raw_value_ref(), raw_result, nullptr /* example_unsandboxed_ptr */, this /* sandbox_ptr */); return wrapped_result; } } // Useful in the porting stage to temporarily allow non tainted pointers to go // through. This will only ever work in the rlbox_noop_sandbox. Any sandbox // that actually enforces isolation will crash here. template tainted UNSAFE_accept_pointer(T2 ptr) { static_assert(std::is_pointer_v, "UNSAFE_accept_pointer expects a pointer param"); tainted ret; ret.assign_raw_pointer(*this, ptr); return ret; } template using T_Cb_no_wrap = detail::rlbox_remove_wrapper_t( detail::rlbox_remove_wrapper_t...); template sandbox_callback*, T_Sbx> register_callback(T_Ret (*)()) { rlbox_detail_static_fail_because( detail::true_v, "Modify the callback to change the first parameter to a sandbox. " "For instance if a callback has type\n\n" "int foo() {...}\n\n" "Change this to \n\n" "tainted foo(rlbox_sandbox& sandbox) {...}\n"); // this is never executed, but we need it for the function to type-check std::abort(); } /** * @brief Expose a callback function to the sandboxed code. * * @param func_ptr The callback to expose. * * @tparam T_RL Sandbox reference type (first argument). * @tparam T_Ret Return type of callback. Must be tainted or void. * @tparam T_Args Types of remaining callback arguments. Must be tainted. * * @return Wrapped callback function pointer that can be passed to the * sandbox. */ template sandbox_callback*, T_Sbx> register_callback( T_Ret (*func_ptr)(T_RL, T_Args...)) { // Some branches don't use the param RLBOX_UNUSED(func_ptr); if_constexpr_named(cond1, !std::is_same_v&>) { rlbox_detail_static_fail_because( cond1, "Modify the callback to change the first parameter to a sandbox. " "For instance if a callback has type\n\n" "int foo(int a, int b) {...}\n\n" "Change this to \n\n" "tainted foo(rlbox_sandbox& sandbox, " "tainted a, tainted b) {...}\n"); } else if_constexpr_named( cond2, !(detail::rlbox_is_tainted_or_opaque_v && ...)) { rlbox_detail_static_fail_because( cond2, "Change all arguments to the callback have to be tainted or " "tainted_opaque. " "For instance if a callback has type\n\n" "int foo(int a, int b) {...}\n\n" "Change this to \n\n" "tainted foo(rlbox_sandbox& sandbox, " "tainted a, tainted b) {...}\n"); } else if_constexpr_named( cond3, (std::is_array_v> || ...)) { rlbox_detail_static_fail_because( cond3, "Change all static array arguments to the callback to be pointers. " "For instance if a callback has type\n\n" "int foo(int a[4]) {...}\n\n" "Change this to \n\n" "tainted foo(rlbox_sandbox& sandbox, " "tainted a) {...}\n"); } else if_constexpr_named( cond4, !(std::is_void_v || detail::rlbox_is_tainted_or_opaque_v)) { rlbox_detail_static_fail_because( cond4, "Change the callback return type to be tainted or tainted_opaque if it " "is not void. " "For instance if a callback has type\n\n" "int foo(int a, int b) {...}\n\n" "Change this to \n\n" "tainted foo(rlbox_sandbox& sandbox, " "tainted a, tainted b) {...}\n"); } else { detail::dynamic_check( sandbox_created.load() == Sandbox_Status::CREATED, "register_callback called without sandbox creation"); // Need unique key for each callback we register - just use the func addr void* unique_key = reinterpret_cast(func_ptr); // Make sure that the user hasn't previously registered this function... // If they have, we would returning 2 owning types (sandbox_callback) to // the same callback which would be bad { std::lock_guard lock(callback_lock); bool exists = std::find(callback_keys.begin(), callback_keys.end(), unique_key) != callback_keys.end(); detail::dynamic_check( !exists, "You have previously already registered this callback."); callback_keys.push_back(unique_key); } auto callback_interceptor = sandbox_callback_interceptor, detail::rlbox_remove_wrapper_t...>; auto callback_trampoline = this->template impl_register_callback< detail::convert_to_sandbox_equivalent_t< detail::rlbox_remove_wrapper_t, T_Sbx>, detail::convert_to_sandbox_equivalent_t< detail::rlbox_remove_wrapper_t, T_Sbx>...>(unique_key, reinterpret_cast(callback_interceptor)); auto tainted_func_ptr = reinterpret_cast< detail::rlbox_tainted_opaque_to_tainted_t (*)( T_RL, detail::rlbox_tainted_opaque_to_tainted_t...)>( reinterpret_cast(func_ptr)); auto ret = sandbox_callback*, T_Sbx>( this, tainted_func_ptr, callback_interceptor, callback_trampoline, unique_key); return ret; } } // this is an internal function invoked from macros, so it has be public template inline tainted INTERNAL_get_sandbox_function_name( const char* func_name) { return INTERNAL_get_sandbox_function_ptr( internal_lookup_symbol(func_name)); } // this is an internal function invoked from macros, so it has be public template inline tainted INTERNAL_get_sandbox_function_ptr(void* func_ptr) { return tainted::internal_factory(reinterpret_cast(func_ptr)); } /** * @brief Create a "fake" pointer referring to a location in the application * memory * * @param ptr The pointer to refer to * * @return The app_pointer object that refers to this location. */ template app_pointer get_app_pointer(T* ptr) { auto max_ptr = (typename T_Sbx::T_PointerType)(get_total_memory() - 1); auto idx = app_ptr_map.get_app_pointer_idx((void*)ptr, max_ptr); auto idx_as_ptr = this->template impl_get_unsandboxed_pointer(idx); // Right now we simply assume that any integer can be converted to a valid // pointer in the sandbox This may not be true for some sandboxing mechanism // plugins in the future In this case, we will have to come up with // something more clever to construct indexes that look like valid pointers // Add a check for now to make sure things work fine detail::dynamic_check(is_pointer_in_sandbox_memory(idx_as_ptr), "App pointers are not currently supported for this " "rlbox sandbox plugin. Please file a bug."); auto ret = app_pointer( &app_ptr_map, idx, reinterpret_cast(idx_as_ptr)); return ret; } /** * @brief The mirror of get_app_pointer. Take a tainted pointer which is * actually an app_pointer, and get the application location being pointed to * * @param tainted_ptr The tainted pointer that is actually an app_pointer * * @return The original location being referred to by the app_ptr */ template T* lookup_app_ptr(tainted tainted_ptr) { auto idx = tainted_ptr.get_raw_sandbox_value(*this); void* ret = app_ptr_map.lookup_index(idx); return reinterpret_cast(ret); } #ifdef RLBOX_MEASURE_TRANSITION_TIMES inline std::vector& process_and_get_transition_times() { return transition_times; } inline int64_t get_total_ns_time_in_sandbox_and_transitions() { int64_t ret = 0; for (auto& transition_time : transition_times) { if (transition_time.invoke == rlbox_transition::INVOKE) { ret += transition_time.time; } else { ret -= transition_time.time; } } return ret; } inline void clear_transition_times() { transition_times.clear(); } #endif }; #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" #elif defined(__GNUC__) || defined(__GNUG__) // Can't turn off the variadic macro warning emitted from -pedantic so use a // hack to stop GCC emitting warnings for the reminder of this file # pragma GCC system_header #elif defined(_MSC_VER) // Doesn't seem to emit the warning #else // Don't know the compiler... just let it go through #endif /** * @def invoke_sandbox_function * @brief Call sandbox function. * * @param func_name The sandboxed library function to call. * @param ... Arguments to function should be simple or tainted values. * @return Tainted value or void. */ #ifdef RLBOX_USE_STATIC_CALLS # define sandbox_lookup_symbol_helper(prefix, func_name) prefix(func_name) # define invoke_sandbox_function(func_name, ...) \ template INTERNAL_invoke_with_func_ptr( \ #func_name, \ sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name), \ ##__VA_ARGS__) # define get_sandbox_function_address(func_name) \ template INTERNAL_get_sandbox_function_ptr( \ sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name)) #else # define invoke_sandbox_function(func_name, ...) \ template INTERNAL_invoke_with_func_name( \ #func_name, ##__VA_ARGS__) # define get_sandbox_function_address(func_name) \ template INTERNAL_get_sandbox_function_name(#func_name) #endif #define sandbox_invoke(sandbox, func_name, ...) \ (sandbox).invoke_sandbox_function(func_name, ##__VA_ARGS__) #define sandbox_function_address(sandbox, func_name) \ (sandbox).get_sandbox_function_address(func_name) #if defined(__clang__) # pragma clang diagnostic pop #else #endif }