From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- .../rlbox_lucet_sandbox/include/lucet_sandbox.h | 71 ++ .../include/rlbox_lucet_sandbox.hpp | 881 +++++++++++++++++++++ 2 files changed, 952 insertions(+) create mode 100644 third_party/rust/rlbox_lucet_sandbox/include/lucet_sandbox.h create mode 100644 third_party/rust/rlbox_lucet_sandbox/include/rlbox_lucet_sandbox.hpp (limited to 'third_party/rust/rlbox_lucet_sandbox/include') diff --git a/third_party/rust/rlbox_lucet_sandbox/include/lucet_sandbox.h b/third_party/rust/rlbox_lucet_sandbox/include/lucet_sandbox.h new file mode 100644 index 0000000000..822a862d0f --- /dev/null +++ b/third_party/rust/rlbox_lucet_sandbox/include/lucet_sandbox.h @@ -0,0 +1,71 @@ +#ifndef LUCET_SANDBOX_H +#define LUCET_SANDBOX_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct LucetSandboxInstance; +typedef struct LucetSandboxInstance LucetSandboxInstance; + +enum LucetValueType { + LucetValueType_I32, + LucetValueType_I64, + LucetValueType_F32, + LucetValueType_F64, + LucetValueType_Void +}; + +typedef struct { + enum LucetValueType val_type; + union { + uint32_t u32; + uint64_t u64; + float f32; + double f64; + }; +} LucetValue; + +typedef struct { + enum LucetValueType ret; + uint32_t parameter_cnt; + LucetValueType *parameters; +} LucetFunctionSignature; + +typedef struct { + uint64_t ty; + uint64_t rf; +} LucetFunctionTableElement; + +typedef struct { + LucetFunctionTableElement *data; + size_t length; +} LucetFunctionTable; + +void lucet_ensure_linked(); +LucetSandboxInstance *lucet_load_module(const char *lucet_module_path, + bool allow_stdio); +void lucet_drop_module(LucetSandboxInstance *inst); + +void* lucet_lookup_function(LucetSandboxInstance *inst, + const char *fn_name); +void lucet_set_curr_instance(LucetSandboxInstance *inst); +void lucet_clear_curr_instance(LucetSandboxInstance *inst); + +uintptr_t lucet_get_reserved_callback_slot_val(void *inst, + uint32_t slot_number); +LucetFunctionTable lucet_get_function_pointer_table(void *inst); +int32_t lucet_get_function_type_index(void *inst, LucetFunctionSignature csig); + +void *lucet_get_heap_base(LucetSandboxInstance *inst); +size_t lucet_get_heap_size(LucetSandboxInstance *inst); +uint32_t lucet_get_export_function_id(void *inst, void *unsandboxed_ptr); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/third_party/rust/rlbox_lucet_sandbox/include/rlbox_lucet_sandbox.hpp b/third_party/rust/rlbox_lucet_sandbox/include/rlbox_lucet_sandbox.hpp new file mode 100644 index 0000000000..43d6e3715e --- /dev/null +++ b/third_party/rust/rlbox_lucet_sandbox/include/rlbox_lucet_sandbox.hpp @@ -0,0 +1,881 @@ +#pragma once + +#include "lucet_sandbox.h" + +#include +#include +#include +#include +#include +#include +// RLBox allows applications to provide a custom shared lock implementation +#ifndef RLBOX_USE_CUSTOM_SHARED_LOCK +# include +#endif +#include +#include +#include + +#define RLBOX_LUCET_UNUSED(...) (void)__VA_ARGS__ + +// Use the same convention as rlbox to allow applications to customize the +// shared lock +#ifndef RLBOX_USE_CUSTOM_SHARED_LOCK +# define RLBOX_SHARED_LOCK(name) std::shared_timed_mutex name +# define RLBOX_ACQUIRE_SHARED_GUARD(name, ...) \ + std::shared_lock name(__VA_ARGS__) +# define RLBOX_ACQUIRE_UNIQUE_GUARD(name, ...) \ + std::unique_lock name(__VA_ARGS__) +#else +# if !defined(RLBOX_SHARED_LOCK) || !defined(RLBOX_ACQUIRE_SHARED_GUARD) || \ + !defined(RLBOX_ACQUIRE_UNIQUE_GUARD) +# error \ + "RLBOX_USE_CUSTOM_SHARED_LOCK defined but missing definitions for RLBOX_SHARED_LOCK, RLBOX_ACQUIRE_SHARED_GUARD, RLBOX_ACQUIRE_UNIQUE_GUARD" +# endif +#endif + +namespace rlbox { + +namespace detail { + // relying on the dynamic check settings (exception vs abort) in the rlbox lib + inline void dynamic_check(bool check, const char* const msg); +} + +namespace lucet_detail { + + template + constexpr bool false_v = false; + + // https://stackoverflow.com/questions/6512019/can-we-get-the-type-of-a-lambda-argument + namespace return_argument_detail { + template + Ret helper(Ret (*)(Rest...)); + + template + Ret helper(Ret (F::*)(Rest...)); + + template + Ret helper(Ret (F::*)(Rest...) const); + + template + decltype(helper(&F::operator())) helper(F); + } // namespace return_argument_detail + + template + using return_argument = + decltype(return_argument_detail::helper(std::declval())); + + /////////////////////////////////////////////////////////////// + + // https://stackoverflow.com/questions/37602057/why-isnt-a-for-loop-a-compile-time-expression + namespace compile_time_for_detail { + template + struct num + { + static const constexpr auto value = N; + }; + + template + inline void compile_time_for_helper(F func, std::index_sequence) + { + (func(num{}), ...); + } + } // namespace compile_time_for_detail + + template + inline void compile_time_for(F func) + { + compile_time_for_detail::compile_time_for_helper( + func, std::make_index_sequence()); + } + + /////////////////////////////////////////////////////////////// + + template + struct convert_type_to_wasm_type + { + static_assert(std::is_void_v, "Missing specialization"); + using type = void; + static constexpr enum LucetValueType lucet_type = LucetValueType_Void; + }; + + template + struct convert_type_to_wasm_type< + T, + std::enable_if_t<(std::is_integral_v || std::is_enum_v)&&sizeof(T) <= + sizeof(uint32_t)>> + { + using type = uint32_t; + static constexpr enum LucetValueType lucet_type = LucetValueType_I32; + }; + + template + struct convert_type_to_wasm_type< + T, + std::enable_if_t<(std::is_integral_v || + std::is_enum_v)&&sizeof(uint32_t) < sizeof(T) && + sizeof(T) <= sizeof(uint64_t)>> + { + using type = uint64_t; + static constexpr enum LucetValueType lucet_type = LucetValueType_I64; + }; + + template + struct convert_type_to_wasm_type>> + { + using type = T; + static constexpr enum LucetValueType lucet_type = LucetValueType_F32; + }; + + template + struct convert_type_to_wasm_type>> + { + using type = T; + static constexpr enum LucetValueType lucet_type = LucetValueType_F64; + }; + + template + struct convert_type_to_wasm_type< + T, + std::enable_if_t || std::is_class_v>> + { + // pointers are 32 bit indexes in wasm + // class paramters are passed as a pointer to an object in the stack or heap + using type = uint32_t; + static constexpr enum LucetValueType lucet_type = LucetValueType_I32; + }; + + /////////////////////////////////////////////////////////////// + + namespace prepend_arg_type_detail { + template + struct helper; + + template + struct helper + { + using type = T_Ret(T_ArgNew, T_Args...); + }; + } + + template + using prepend_arg_type = + typename prepend_arg_type_detail::helper::type; + + /////////////////////////////////////////////////////////////// + + namespace change_return_type_detail { + template + struct helper; + + template + struct helper + { + using type = T_RetNew(T_Args...); + }; + } + + template + using change_return_type = + typename change_return_type_detail::helper::type; + + /////////////////////////////////////////////////////////////// + + namespace change_class_arg_types_detail { + template + struct helper; + + template + struct helper + { + using type = + T_Ret(std::conditional_t, T_ArgNew, T_Args>...); + }; + } + + template + using change_class_arg_types = + typename change_class_arg_types_detail::helper::type; + +} // namespace lucet_detail + +class rlbox_lucet_sandbox; + +struct rlbox_lucet_sandbox_thread_data +{ + rlbox_lucet_sandbox* sandbox; + uint32_t last_callback_invoked; +}; + +#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES + +rlbox_lucet_sandbox_thread_data* get_rlbox_lucet_sandbox_thread_data(); +# define RLBOX_LUCET_SANDBOX_STATIC_VARIABLES() \ + thread_local rlbox::rlbox_lucet_sandbox_thread_data \ + rlbox_lucet_sandbox_thread_info{ 0, 0 }; \ + namespace rlbox { \ + rlbox_lucet_sandbox_thread_data* get_rlbox_lucet_sandbox_thread_data() \ + { \ + return &rlbox_lucet_sandbox_thread_info; \ + } \ + } \ + static_assert(true, "Enforce semi-colon") + +#endif + +class rlbox_lucet_sandbox +{ +public: + using T_LongLongType = int32_t; + using T_LongType = int32_t; + using T_IntType = int32_t; + using T_PointerType = uint32_t; + using T_ShortType = int16_t; + +private: + LucetSandboxInstance* sandbox = nullptr; + uintptr_t heap_base; + void* malloc_index = 0; + void* free_index = 0; + size_t return_slot_size = 0; + T_PointerType return_slot = 0; + + static const size_t MAX_CALLBACKS = 128; + RLBOX_SHARED_LOCK(callback_mutex); + void* callback_unique_keys[MAX_CALLBACKS]{ 0 }; + void* callbacks[MAX_CALLBACKS]{ 0 }; + uint32_t callback_slot_assignment[MAX_CALLBACKS]{ 0 }; + + using TableElementRef = LucetFunctionTableElement*; + struct FunctionTable + { + TableElementRef elements[MAX_CALLBACKS]; + uint32_t slot_number[MAX_CALLBACKS]; + }; + inline static std::mutex callback_table_mutex; + // We need to share the callback slot info across multiple sandbox instances + // that may load the same sandboxed library. Thus if the sandboxed library is + // already in the memory space, we should just use the previously saved info + // as the load is destroys the callback info. Once all instances of the + // library is unloaded, the sandboxed library is removed from the address + // space and thus we can "reset" our state. The semantics of shared and weak + // pointers ensure this and will automatically release the memory after all + // instances are released. + inline static std::map> + shared_callback_slots; + std::shared_ptr callback_slots = nullptr; + // However, if the library is also loaded externally in the application, then + // we don't know when we can ever "reset". In such scenarios, we are better of + // never throwing away the callback info, rather than figuring out + // what/why/when the application is loading or unloading the sandboxed + // library. An extra reference to the shared_ptr will ensure this. + inline static std::vector> + saved_callback_slot_info; + +#ifndef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES + thread_local static inline rlbox_lucet_sandbox_thread_data thread_data{ 0, + 0 }; +#endif + + template + inline auto serialize_to_sandbox(T_ActualRet arg) + { + if constexpr (std::is_class_v) { + // structs returned as pointers into wasm memory/wasm stack + auto ptr = reinterpret_cast( + impl_get_unsandboxed_pointer(arg)); + T_FormalRet ret = *ptr; + return ret; + } else { + return arg; + } + } + + inline std::shared_ptr get_callback_ref_data( + LucetFunctionTable& functionPointerTable) + { + auto callback_slots = std::make_shared(); + + for (size_t i = 0; i < MAX_CALLBACKS; i++) { + uintptr_t reservedVal = + lucet_get_reserved_callback_slot_val(sandbox, i + 1); + + bool found = false; + for (size_t j = 0; j < functionPointerTable.length; j++) { + if (functionPointerTable.data[j].rf == reservedVal) { + functionPointerTable.data[j].rf = 0; + callback_slots->elements[i] = &(functionPointerTable.data[j]); + callback_slots->slot_number[i] = static_cast(j); + found = true; + break; + } + } + + detail::dynamic_check(found, "Unable to intialize callback tables"); + } + + return callback_slots; + } + + inline void reinit_callback_ref_data( + LucetFunctionTable& functionPointerTable, + std::shared_ptr& callback_slots) + { + for (size_t i = 0; i < MAX_CALLBACKS; i++) { + uintptr_t reservedVal = + lucet_get_reserved_callback_slot_val(sandbox, i + 1); + + for (size_t j = 0; j < functionPointerTable.length; j++) { + if (functionPointerTable.data[j].rf == reservedVal) { + functionPointerTable.data[j].rf = 0; + + detail::dynamic_check( + callback_slots->elements[i] == &(functionPointerTable.data[j]) && + callback_slots->slot_number[i] == static_cast(j), + "Sandbox creation error: Error when checking the values of " + "callback slot data"); + + break; + } + } + } + } + + inline void set_callbacks_slots_ref(bool external_loads_exist) + { + LucetFunctionTable functionPointerTable = + lucet_get_function_pointer_table(sandbox); + void* key = functionPointerTable.data; + + std::lock_guard lock(callback_table_mutex); + std::weak_ptr slots = shared_callback_slots[key]; + + if (auto shared_slots = slots.lock()) { + // pointer exists + callback_slots = shared_slots; + // Sometimes, dlopen and process forking seem to act a little weird. + // Writes to the writable page of the dynamic lib section seem to not + // always be propagated (possibly when the dynamic library is opened + // externally - "external_loads_exist")). This occurred in when RLBox was + // used in ASAN builds of Firefox. In general, we take the precaution of + // rechecking this on each sandbox creation. + reinit_callback_ref_data(functionPointerTable, callback_slots); + return; + } + + callback_slots = get_callback_ref_data(functionPointerTable); + shared_callback_slots[key] = callback_slots; + if (external_loads_exist) { + saved_callback_slot_info.push_back(callback_slots); + } + } + + template + static typename lucet_detail::convert_type_to_wasm_type::type + callback_interceptor( + void* /* vmContext */, + typename lucet_detail::convert_type_to_wasm_type::type... params) + { +#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES + auto& thread_data = *get_rlbox_lucet_sandbox_thread_data(); +#endif + thread_data.last_callback_invoked = N; + using T_Func = T_Ret (*)(T_Args...); + T_Func func; + { + RLBOX_ACQUIRE_SHARED_GUARD(lock, thread_data.sandbox->callback_mutex); + func = reinterpret_cast(thread_data.sandbox->callbacks[N]); + } + // Callbacks are invoked through function pointers, cannot use std::forward + // as we don't have caller context for T_Args, which means they are all + // effectively passed by value + return func(thread_data.sandbox->serialize_to_sandbox(params)...); + } + + template + static void callback_interceptor_promoted( + void* /* vmContext */, + typename lucet_detail::convert_type_to_wasm_type::type ret, + typename lucet_detail::convert_type_to_wasm_type::type... params) + { +#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES + auto& thread_data = *get_rlbox_lucet_sandbox_thread_data(); +#endif + thread_data.last_callback_invoked = N; + using T_Func = T_Ret (*)(T_Args...); + T_Func func; + { + RLBOX_ACQUIRE_SHARED_GUARD(lock, thread_data.sandbox->callback_mutex); + func = reinterpret_cast(thread_data.sandbox->callbacks[N]); + } + // Callbacks are invoked through function pointers, cannot use std::forward + // as we don't have caller context for T_Args, which means they are all + // effectively passed by value + auto ret_val = + func(thread_data.sandbox->serialize_to_sandbox(params)...); + // Copy the return value back + auto ret_ptr = reinterpret_cast( + thread_data.sandbox->template impl_get_unsandboxed_pointer(ret)); + *ret_ptr = ret_val; + } + + template + inline T_PointerType get_lucet_type_index( + T_Ret (*/* dummy for template inference */)(T_Args...) = nullptr) const + { + // Class return types as promoted to args + constexpr bool promoted = std::is_class_v; + int32_t type_index; + + if constexpr (promoted) { + LucetValueType ret_type = LucetValueType::LucetValueType_Void; + LucetValueType param_types[] = { + lucet_detail::convert_type_to_wasm_type::lucet_type, + lucet_detail::convert_type_to_wasm_type::lucet_type... + }; + LucetFunctionSignature signature{ ret_type, + sizeof(param_types) / + sizeof(LucetValueType), + &(param_types[0]) }; + type_index = lucet_get_function_type_index(sandbox, signature); + } else { + LucetValueType ret_type = + lucet_detail::convert_type_to_wasm_type::lucet_type; + LucetValueType param_types[] = { + lucet_detail::convert_type_to_wasm_type::lucet_type... + }; + LucetFunctionSignature signature{ ret_type, + sizeof(param_types) / + sizeof(LucetValueType), + &(param_types[0]) }; + type_index = lucet_get_function_type_index(sandbox, signature); + } + + return type_index; + } + + void ensure_return_slot_size(size_t size) + { + if (size > return_slot_size) { + if (return_slot_size) { + impl_free_in_sandbox(return_slot); + } + return_slot = impl_malloc_in_sandbox(size); + detail::dynamic_check( + return_slot != 0, + "Error initializing return slot. Sandbox may be out of memory!"); + return_slot_size = size; + } + } + +protected: + // Set external_loads_exist to true, if the host application loads the + // library lucet_module_path outside of rlbox_lucet_sandbox such as via dlopen + // or the Windows equivalent + inline void impl_create_sandbox(const char* lucet_module_path, + bool external_loads_exist, + bool allow_stdio) + { + detail::dynamic_check(sandbox == nullptr, "Sandbox already initialized"); + sandbox = lucet_load_module(lucet_module_path, allow_stdio); + detail::dynamic_check(sandbox != nullptr, "Sandbox could not be created"); + + heap_base = reinterpret_cast(impl_get_memory_location()); + // Check that the address space is larger than the sandbox heap i.e. 4GB + // sandbox heap, host has to have more than 4GB + static_assert(sizeof(uintptr_t) > sizeof(T_PointerType)); + // Check that the heap is aligned to the pointer size i.e. 32-bit pointer => + // aligned to 4GB. The implementations of + // impl_get_unsandboxed_pointer_no_ctx and impl_get_sandboxed_pointer_no_ctx + // below rely on this. + uintptr_t heap_offset_mask = std::numeric_limits::max(); + detail::dynamic_check((heap_base & heap_offset_mask) == 0, + "Sandbox heap not aligned to 4GB"); + + // cache these for performance + malloc_index = impl_lookup_symbol("malloc"); + free_index = impl_lookup_symbol("free"); + + set_callbacks_slots_ref(external_loads_exist); + } + + inline void impl_create_sandbox(const char* lucet_module_path) + { + // Default is to assume that no external code will load the wasm library as + // this is usually the case + const bool external_loads_exist = false; + const bool allow_stdio = true; + impl_create_sandbox(lucet_module_path, external_loads_exist, allow_stdio); + } + + inline void impl_destroy_sandbox() { + if (return_slot_size) { + impl_free_in_sandbox(return_slot); + } + lucet_drop_module(sandbox); + } + + template + inline void* impl_get_unsandboxed_pointer(T_PointerType p) const + { + if constexpr (std::is_function_v>) { + LucetFunctionTable functionPointerTable = + lucet_get_function_pointer_table(sandbox); + if (p >= functionPointerTable.length) { + // Received out of range function pointer + return nullptr; + } + auto ret = functionPointerTable.data[p].rf; + return reinterpret_cast(static_cast(ret)); + } else { + return reinterpret_cast(heap_base + p); + } + } + + template + inline T_PointerType impl_get_sandboxed_pointer(const void* p) const + { + if constexpr (std::is_function_v>) { + // p is a pointer to a function internal to the lucet module + // we need to either + // 1) find the indirect function slot this is registered and return the + // slot number. For this we need to scan the full indirect function table, + // not just the portion we have reserved for callbacks. + // 2) in the scenario this function has not ever been listed as an + // indirect function, we need to register this like a normal callback. + // However, unlike callbacks, we will not require the user to unregister + // this. Instead, this permenantly takes up a callback slot. + LucetFunctionTable functionPointerTable = + lucet_get_function_pointer_table(sandbox); + std::lock_guard lock(callback_table_mutex); + + // Scenario 1 described above + ssize_t empty_slot = -1; + for (size_t i = 0; i < functionPointerTable.length; i++) { + if (functionPointerTable.data[i].rf == reinterpret_cast(p)) { + return static_cast(i); + } else if (functionPointerTable.data[i].rf == 0 && empty_slot == -1) { + // found an empty slot. Save it, as we may use it later. + empty_slot = i; + } + } + + // Scenario 2 described above + detail::dynamic_check( + empty_slot != -1, + "Could not find an empty slot in sandbox function table. This would " + "happen if you have registered too many callbacks, or unsandboxed " + "too many function pointers. You can file a bug if you want to " + "increase the maximum allowed callbacks or unsadnboxed functions " + "pointers"); + T dummy = nullptr; + int32_t type_index = get_lucet_type_index(dummy); + functionPointerTable.data[empty_slot].ty = type_index; + functionPointerTable.data[empty_slot].rf = reinterpret_cast(p); + return empty_slot; + + } else { + return static_cast(reinterpret_cast(p)); + } + } + + template + static inline void* impl_get_unsandboxed_pointer_no_ctx( + T_PointerType p, + const void* example_unsandboxed_ptr, + rlbox_lucet_sandbox* (*expensive_sandbox_finder)( + const void* example_unsandboxed_ptr)) + { + if constexpr (std::is_function_v>) { + // swizzling function pointers needs access to the function pointer tables + // and thus cannot be done without context + auto sandbox = expensive_sandbox_finder(example_unsandboxed_ptr); + return sandbox->impl_get_unsandboxed_pointer(p); + } else { + // grab the memory base from the example_unsandboxed_ptr + uintptr_t heap_base_mask = + std::numeric_limits::max() & + ~(static_cast(std::numeric_limits::max())); + uintptr_t computed_heap_base = + reinterpret_cast(example_unsandboxed_ptr) & heap_base_mask; + uintptr_t ret = computed_heap_base | p; + return reinterpret_cast(ret); + } + } + + template + static inline T_PointerType impl_get_sandboxed_pointer_no_ctx( + const void* p, + const void* example_unsandboxed_ptr, + rlbox_lucet_sandbox* (*expensive_sandbox_finder)( + const void* example_unsandboxed_ptr)) + { + if constexpr (std::is_function_v>) { + // swizzling function pointers needs access to the function pointer tables + // and thus cannot be done without context + auto sandbox = expensive_sandbox_finder(example_unsandboxed_ptr); + return sandbox->impl_get_sandboxed_pointer(p); + } else { + // Just clear the memory base to leave the offset + RLBOX_LUCET_UNUSED(example_unsandboxed_ptr); + uintptr_t ret = reinterpret_cast(p) & + std::numeric_limits::max(); + return static_cast(ret); + } + } + + static inline bool impl_is_in_same_sandbox(const void* p1, const void* p2) + { + uintptr_t heap_base_mask = std::numeric_limits::max() & + ~(std::numeric_limits::max()); + return (reinterpret_cast(p1) & heap_base_mask) == + (reinterpret_cast(p2) & heap_base_mask); + } + + inline bool impl_is_pointer_in_sandbox_memory(const void* p) + { + size_t length = impl_get_total_memory(); + uintptr_t p_val = reinterpret_cast(p); + return p_val >= heap_base && p_val < (heap_base + length); + } + + inline bool impl_is_pointer_in_app_memory(const void* p) + { + return !(impl_is_pointer_in_sandbox_memory(p)); + } + + inline size_t impl_get_total_memory() { return lucet_get_heap_size(sandbox); } + + inline void* impl_get_memory_location() + { + return lucet_get_heap_base(sandbox); + } + + void* impl_lookup_symbol(const char* func_name) + { + return lucet_lookup_function(sandbox, func_name); + } + + template + auto impl_invoke_with_func_ptr(T_Converted* func_ptr, T_Args&&... params) + { +#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES + auto& thread_data = *get_rlbox_lucet_sandbox_thread_data(); +#endif + thread_data.sandbox = this; + lucet_set_curr_instance(sandbox); + + // WASM functions are mangled in the following manner + // 1. All primitive types are left as is and follow an LP32 machine model + // (as opposed to the possibly 64-bit application) + // 2. All pointers are changed to u32 types + // 3. Returned class are returned as an out parameter before the actual + // function parameters + // 4. All class parameters are passed as pointers (u32 types) + // 5. The heap address is passed in as the first argument to the function + // + // RLBox accounts for the first 2 differences in T_Converted type, but we + // need to handle the rest + + // Handle point 3 + using T_Ret = lucet_detail::return_argument; + if constexpr (std::is_class_v) { + using T_Conv1 = lucet_detail::change_return_type; + using T_Conv2 = lucet_detail::prepend_arg_type; + auto func_ptr_conv = + reinterpret_cast(reinterpret_cast(func_ptr)); + ensure_return_slot_size(sizeof(T_Ret)); + impl_invoke_with_func_ptr(func_ptr_conv, return_slot, params...); + + auto ptr = reinterpret_cast( + impl_get_unsandboxed_pointer(return_slot)); + T_Ret ret = *ptr; + return ret; + } + + // Handle point 4 + constexpr size_t alloc_length = [&] { + if constexpr (sizeof...(params) > 0) { + return ((std::is_class_v ? 1 : 0) + ...); + } else { + return 0; + } + }(); + + // 0 arg functions create 0 length arrays which is not allowed + T_PointerType allocations_buff[alloc_length == 0 ? 1 : alloc_length]; + T_PointerType* allocations = allocations_buff; + + auto serialize_class_arg = + [&](auto arg) -> std::conditional_t, + T_PointerType, + decltype(arg)> { + using T_Arg = decltype(arg); + if constexpr (std::is_class_v) { + auto slot = impl_malloc_in_sandbox(sizeof(T_Arg)); + auto ptr = + reinterpret_cast(impl_get_unsandboxed_pointer(slot)); + *ptr = arg; + allocations[0] = slot; + allocations++; + return slot; + } else { + return arg; + } + }; + + // 0 arg functions don't use serialize + RLBOX_LUCET_UNUSED(serialize_class_arg); + + using T_ConvNoClass = + lucet_detail::change_class_arg_types; + + // Handle Point 5 + using T_ConvHeap = lucet_detail::prepend_arg_type; + + // Function invocation + auto func_ptr_conv = + reinterpret_cast(reinterpret_cast(func_ptr)); + + using T_NoVoidRet = + std::conditional_t, uint32_t, T_Ret>; + T_NoVoidRet ret; + + if constexpr (std::is_void_v) { + RLBOX_LUCET_UNUSED(ret); + func_ptr_conv(heap_base, serialize_class_arg(params)...); + } else { + ret = func_ptr_conv(heap_base, serialize_class_arg(params)...); + } + + for (size_t i = 0; i < alloc_length; i++) { + impl_free_in_sandbox(allocations_buff[i]); + } + + if constexpr (!std::is_void_v) { + return ret; + } + } + + inline T_PointerType impl_malloc_in_sandbox(size_t size) + { + detail::dynamic_check(size <= std::numeric_limits::max(), + "Attempting to malloc more than the heap size"); + using T_Func = void*(size_t); + using T_Converted = T_PointerType(uint32_t); + T_PointerType ret = impl_invoke_with_func_ptr( + reinterpret_cast(malloc_index), + static_cast(size)); + return ret; + } + + inline void impl_free_in_sandbox(T_PointerType p) + { + using T_Func = void(void*); + using T_Converted = void(T_PointerType); + impl_invoke_with_func_ptr( + reinterpret_cast(free_index), p); + } + + template + inline T_PointerType impl_register_callback(void* key, void* callback) + { + int32_t type_index = get_lucet_type_index(); + + detail::dynamic_check( + type_index != -1, + "Could not find lucet type for callback signature. This can " + "happen if you tried to register a callback whose signature " + "does not correspond to any callbacks used in the library."); + + bool found = false; + uint32_t found_loc = 0; + uint32_t slot_number = 0; + + { + std::lock_guard lock(callback_table_mutex); + + // need a compile time for loop as we we need I to be a compile time value + // this is because we are setting the I'th callback ineterceptor + lucet_detail::compile_time_for([&](auto I) { + constexpr auto i = I.value; + if (!found && callback_slots->elements[i]->rf == 0) { + found = true; + found_loc = i; + slot_number = callback_slots->slot_number[i]; + + void* chosen_interceptor; + if constexpr (std::is_class_v) { + chosen_interceptor = reinterpret_cast( + callback_interceptor_promoted); + } else { + chosen_interceptor = reinterpret_cast( + callback_interceptor); + } + callback_slots->elements[i]->ty = type_index; + callback_slots->elements[i]->rf = + reinterpret_cast(chosen_interceptor); + } + }); + } + + detail::dynamic_check( + found, + "Could not find an empty slot in sandbox function table. This would " + "happen if you have registered too many callbacks, or unsandboxed " + "too many function pointers. You can file a bug if you want to " + "increase the maximum allowed callbacks or unsadnboxed functions " + "pointers"); + + { + RLBOX_ACQUIRE_UNIQUE_GUARD(lock, callback_mutex); + callback_unique_keys[found_loc] = key; + callbacks[found_loc] = callback; + callback_slot_assignment[found_loc] = slot_number; + } + + return static_cast(slot_number); + } + + static inline std::pair + impl_get_executed_callback_sandbox_and_key() + { +#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES + auto& thread_data = *get_rlbox_lucet_sandbox_thread_data(); +#endif + auto sandbox = thread_data.sandbox; + auto callback_num = thread_data.last_callback_invoked; + void* key = sandbox->callback_unique_keys[callback_num]; + return std::make_pair(sandbox, key); + } + + template + inline void impl_unregister_callback(void* key) + { + bool found = false; + uint32_t i = 0; + { + RLBOX_ACQUIRE_UNIQUE_GUARD(lock, callback_mutex); + for (; i < MAX_CALLBACKS; i++) { + if (callback_unique_keys[i] == key) { + callback_unique_keys[i] = nullptr; + callbacks[i] = nullptr; + callback_slot_assignment[i] = 0; + found = true; + break; + } + } + } + + detail::dynamic_check( + found, "Internal error: Could not find callback to unregister"); + + std::lock_guard shared_lock(callback_table_mutex); + callback_slots->elements[i]->rf = 0; + return; + } +}; + +} // namespace rlbox \ No newline at end of file -- cgit v1.2.3