diff options
Diffstat (limited to 'third_party/rlbox/include/rlbox_stdlib.hpp')
-rw-r--r-- | third_party/rlbox/include/rlbox_stdlib.hpp | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/third_party/rlbox/include/rlbox_stdlib.hpp b/third_party/rlbox/include/rlbox_stdlib.hpp new file mode 100644 index 0000000000..cc60428077 --- /dev/null +++ b/third_party/rlbox/include/rlbox_stdlib.hpp @@ -0,0 +1,329 @@ +#pragma once +// IWYU pragma: private, include "rlbox.hpp" +// IWYU pragma: friend "rlbox_.*\.hpp" + +#include <cstring> +#include <type_traits> + +#include "rlbox_helpers.hpp" +#include "rlbox_types.hpp" +#include "rlbox_unwrap.hpp" +#include "rlbox_wrapper_traits.hpp" + +namespace rlbox { +#define KEEP_CAST_FRIENDLY \ + template<typename T_C_Lhs, \ + typename T_C_Rhs, \ + typename T_C_Sbx, \ + template<typename, typename> \ + typename T_C_Wrap> \ + friend inline tainted<T_C_Lhs, T_C_Sbx> sandbox_reinterpret_cast( \ + const T_C_Wrap<T_C_Rhs, T_C_Sbx>& rhs) noexcept; \ + \ + template<typename T_C_Lhs, \ + typename T_C_Rhs, \ + typename T_C_Sbx, \ + template<typename, typename> \ + typename T_C_Wrap> \ + friend inline tainted<T_C_Lhs, T_C_Sbx> sandbox_const_cast( \ + const T_C_Wrap<T_C_Rhs, T_C_Sbx>& rhs) noexcept; \ + \ + template<typename T_C_Lhs, \ + typename T_C_Rhs, \ + typename T_C_Sbx, \ + template<typename, typename> \ + typename T_C_Wrap> \ + friend inline tainted<T_C_Lhs, T_C_Sbx> sandbox_static_cast( \ + const T_C_Wrap<T_C_Rhs, T_C_Sbx>& rhs) noexcept; + +/** + * @brief The equivalent of a reinterpret_cast but operates on sandboxed values. + */ +template<typename T_Lhs, + typename T_Rhs, + typename T_Sbx, + template<typename, typename> + typename T_Wrap> +inline tainted<T_Lhs, T_Sbx> sandbox_reinterpret_cast( + const T_Wrap<T_Rhs, T_Sbx>& rhs) noexcept +{ + static_assert(detail::rlbox_is_wrapper_v<T_Wrap<T_Rhs, T_Sbx>> && + std::is_pointer_v<T_Lhs> && std::is_pointer_v<T_Rhs>, + "sandbox_reinterpret_cast on incompatible types"); + + tainted<T_Rhs, T_Sbx> taintedVal = rhs; + auto raw = reinterpret_cast<T_Lhs>(taintedVal.INTERNAL_unverified_safe()); + auto ret = tainted<T_Lhs, T_Sbx>::internal_factory(raw); + return ret; +} + +/** + * @brief The equivalent of a const_cast but operates on sandboxed values. + */ +template<typename T_Lhs, + typename T_Rhs, + typename T_Sbx, + template<typename, typename> + typename T_Wrap> +inline tainted<T_Lhs, T_Sbx> sandbox_const_cast( + const T_Wrap<T_Rhs, T_Sbx>& rhs) noexcept +{ + static_assert(detail::rlbox_is_wrapper_v<T_Wrap<T_Rhs, T_Sbx>>, + "sandbox_const_cast on incompatible types"); + + tainted<T_Rhs, T_Sbx> taintedVal = rhs; + auto raw = const_cast<T_Lhs>(taintedVal.INTERNAL_unverified_safe()); + auto ret = tainted<T_Lhs, T_Sbx>::internal_factory(raw); + return ret; +} + +/** + * @brief The equivalent of a static_cast but operates on sandboxed values. + */ +template<typename T_Lhs, + typename T_Rhs, + typename T_Sbx, + template<typename, typename> + typename T_Wrap> +inline tainted<T_Lhs, T_Sbx> sandbox_static_cast( + const T_Wrap<T_Rhs, T_Sbx>& rhs) noexcept +{ + static_assert(detail::rlbox_is_wrapper_v<T_Wrap<T_Rhs, T_Sbx>>, + "sandbox_static_cast on incompatible types"); + + tainted<T_Rhs, T_Sbx> taintedVal = rhs; + auto raw = static_cast<T_Lhs>(taintedVal.INTERNAL_unverified_safe()); + auto ret = tainted<T_Lhs, T_Sbx>::internal_factory(raw); + return ret; +} + +/** + * @brief Fill sandbox memory with a constant byte. + */ +template<typename T_Sbx, + typename T_Rhs, + typename T_Val, + typename T_Num, + template<typename, typename> + typename T_Wrap> +inline T_Wrap<T_Rhs*, T_Sbx> memset(rlbox_sandbox<T_Sbx>& sandbox, + T_Wrap<T_Rhs*, T_Sbx> ptr, + T_Val value, + T_Num num) +{ + + static_assert(detail::rlbox_is_tainted_or_vol_v<T_Wrap<T_Rhs, T_Sbx>>, + "memset called on non wrapped type"); + + static_assert(!std::is_const_v<T_Rhs>, "Destination is const"); + + auto num_val = detail::unwrap_value(num); + detail::dynamic_check(num_val <= sandbox.get_total_memory(), + "Called memset for memory larger than the sandbox"); + + tainted<T_Rhs*, T_Sbx> ptr_tainted = ptr; + void* dest_start = ptr_tainted.INTERNAL_unverified_safe(); + detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(dest_start, num_val); + + std::memset(dest_start, detail::unwrap_value(value), num_val); + return ptr; +} + +/** + * @brief Copy to sandbox memory area. Note that memcpy is meant to be called on + * byte arrays does not adjust data according to ABI differences. If the + * programmer does accidentally call memcpy on buffers that needs ABI + * adjustment, this may cause compatibility issues, but will not cause a + * security issue as the destination is always a tainted or tainted_volatile + * pointer + */ +template<typename T_Sbx, + typename T_Rhs, + typename T_Lhs, + typename T_Num, + template<typename, typename> + typename T_Wrap> +inline T_Wrap<T_Rhs*, T_Sbx> memcpy(rlbox_sandbox<T_Sbx>& sandbox, + T_Wrap<T_Rhs*, T_Sbx> dest, + T_Lhs src, + T_Num num) +{ + + static_assert(detail::rlbox_is_tainted_or_vol_v<T_Wrap<T_Rhs, T_Sbx>>, + "memcpy called on non wrapped type"); + + static_assert(!std::is_const_v<T_Rhs>, "Destination is const"); + + auto num_val = detail::unwrap_value(num); + detail::dynamic_check(num_val <= sandbox.get_total_memory(), + "Called memcpy for memory larger than the sandbox"); + + tainted<T_Rhs*, T_Sbx> dest_tainted = dest; + void* dest_start = dest_tainted.INTERNAL_unverified_safe(); + detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(dest_start, num_val); + + // src also needs to be checked, as we don't want to allow a src rand to start + // inside the sandbox and end outside, and vice versa + // src may or may not be a wrapper, so use unwrap_value + const void* src_start = detail::unwrap_value(src); + detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(src_start, num_val); + + std::memcpy(dest_start, src_start, num_val); + + return dest; +} + +/** + * @brief Compare data in sandbox memory area. + */ +template<typename T_Sbx, typename T_Rhs, typename T_Lhs, typename T_Num> +inline tainted_int_hint memcmp(rlbox_sandbox<T_Sbx>& sandbox, + T_Rhs&& dest, + T_Lhs&& src, + T_Num&& num) +{ + static_assert( + detail::rlbox_is_tainted_or_vol_v<detail::remove_cv_ref_t<T_Rhs>> || + detail::rlbox_is_tainted_or_vol_v<detail::remove_cv_ref_t<T_Lhs>>, + "memcmp called on non wrapped type"); + + auto num_val = detail::unwrap_value(num); + detail::dynamic_check(num_val <= sandbox.get_total_memory(), + "Called memcmp for memory larger than the sandbox"); + + void* dest_start = dest.INTERNAL_unverified_safe(); + detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(dest_start, num_val); + + // src also needs to be checked, as we don't want to allow a src rand to start + // inside the sandbox and end outside, and vice versa + // src may or may not be a wrapper, so use unwrap_value + const void* src_start = detail::unwrap_value(src); + detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(src_start, num_val); + + int ret = std::memcmp(dest_start, src_start, num_val); + tainted_int_hint converted_ret(ret); + return converted_ret; +} + +/** + * @brief This function either + * - copies the given buffer into the sandbox calling delete on the src + * OR + * - if the sandbox allows, adds the buffer to the existing sandbox memory + * @param sandbox Target sandbox + * @param src Raw pointer to the buffer + * @param num Number of T-sized elements in the buffer + * @param free_source_on_copy If the source buffer was copied, this variable + * controls whether copy_memory_or_grant_access should call delete on the src. + * This calls delete[] if num > 1. + * @param copied out parameter indicating if the source was copied or transfered + */ +template<typename T_Sbx, typename T> +tainted<T*, T_Sbx> copy_memory_or_grant_access(rlbox_sandbox<T_Sbx>& sandbox, + T* src, + size_t num, + bool free_source_on_copy, + bool& copied) +{ + copied = false; + + // This function is meant for byte buffers only - so char and char16 + static_assert(sizeof(T) <= 2); + + // overflow ok + size_t source_size = num * sizeof(T); + + // sandbox can grant access if it includes the following line + // using can_grant_deny_access = void; + if constexpr (detail::has_member_using_can_grant_deny_access_v<T_Sbx>) { + detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(src, source_size); + + bool success; + auto ret = sandbox.INTERNAL_grant_access(src, num, success); + if (success) { + return ret; + } + } + + // Malloc in sandbox takes a uint32_t as the parameter, need a bounds check + detail::dynamic_check(num <= std::numeric_limits<uint32_t>::max(), + "Granting access too large a region"); + using T_nocv = std::remove_cv_t<T>; + tainted<T_nocv*, T_Sbx> copy = + sandbox.template malloc_in_sandbox<T_nocv>(static_cast<uint32_t>(num)); + if (!copy) { + return nullptr; + } + + rlbox::memcpy(sandbox, copy, src, source_size); + if (free_source_on_copy) { + free(const_cast<void*>(reinterpret_cast<const void*>(src))); + } + + copied = true; + return sandbox_const_cast<T*>(copy); +} + +/** + * @brief This function either + * - copies the given buffer out of the sandbox calling free_in_sandbox on the + * src + * OR + * - if the sandbox allows, moves the buffer out of existing sandbox memory + * @param sandbox Target sandbox + * @param src Raw pointer to the buffer + * @param num Number of T-sized elements in the buffer + * @param free_source_on_copy If the source buffer was copied, this variable + * controls whether copy_memory_or_deny_access should call delete on the src. + * This calls delete[] if num > 1. + * @param copied out parameter indicating if the source was copied or transfered + */ +template<typename T_Sbx, + typename T, + template<typename, typename> + typename T_Wrap> +T* copy_memory_or_deny_access(rlbox_sandbox<T_Sbx>& sandbox, + T_Wrap<T*, T_Sbx> src, + size_t num, + bool free_source_on_copy, + bool& copied) +{ + copied = false; + + // This function is meant for byte buffers only - so char and char16 + static_assert(sizeof(T) <= 2); + + // overflow ok + size_t source_size = num * sizeof(T); + + // sandbox can grant access if it includes the following line + // using can_grant_deny_access = void; + if constexpr (detail::has_member_using_can_grant_deny_access_v<T_Sbx>) { + detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>( + src.INTERNAL_unverified_safe(), source_size); + + bool success; + auto ret = sandbox.INTERNAL_deny_access(src, num, success); + if (success) { + return ret; + } + } + + auto copy = static_cast<T*>(malloc(source_size)); + if (!copy) { + return nullptr; + } + + tainted<T*, T_Sbx> src_tainted = src; + char* src_raw = src_tainted.copy_and_verify_buffer_address( + [](uintptr_t val) { return reinterpret_cast<char*>(val); }, num); + std::memcpy(copy, src_raw, source_size); + if (free_source_on_copy) { + sandbox.free_in_sandbox(src); + } + + copied = true; + return copy; +} + +} |