summaryrefslogtreecommitdiffstats
path: root/third_party/rlbox/include/rlbox_policy_types.hpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rlbox/include/rlbox_policy_types.hpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rlbox/include/rlbox_policy_types.hpp')
-rw-r--r--third_party/rlbox/include/rlbox_policy_types.hpp392
1 files changed, 392 insertions, 0 deletions
diff --git a/third_party/rlbox/include/rlbox_policy_types.hpp b/third_party/rlbox/include/rlbox_policy_types.hpp
new file mode 100644
index 0000000000..d2fbc500ee
--- /dev/null
+++ b/third_party/rlbox/include/rlbox_policy_types.hpp
@@ -0,0 +1,392 @@
+#pragma once
+// IWYU pragma: private, include "rlbox.hpp"
+// IWYU pragma: friend "rlbox_.*\.hpp"
+
+#include <type_traits>
+#include <utility>
+
+#include "rlbox_helpers.hpp"
+#include "rlbox_struct_support.hpp"
+#include "rlbox_types.hpp"
+
+namespace rlbox {
+
+namespace callback_detail {
+
+ // Compute the expected type of the callback
+ template<typename T_Sbx, typename T_Ret, typename... T_Args>
+ using T_Cb =
+ std::conditional_t<std::is_void_v<T_Ret>, void, tainted<T_Ret, T_Sbx>> (*)(
+ rlbox_sandbox<T_Sbx>&,
+ tainted<T_Args, T_Sbx>...);
+
+ template<typename T_Sbx, typename T_Ret, typename... T_Args>
+ T_Cb<T_Sbx, T_Ret, T_Args...> callback_type_helper(T_Ret (*)(T_Args...));
+
+ // Compute the expected type of the interceptor
+ template<typename T_Sbx, typename T_Ret, typename... T_Args>
+ using T_I = detail::convert_to_sandbox_equivalent_t<T_Ret, T_Sbx> (*)(
+ detail::convert_to_sandbox_equivalent_t<T_Args, T_Sbx>...);
+
+ template<typename T_Sbx, typename T_Ret, typename... T_Args>
+ T_I<T_Sbx, T_Ret, T_Args...> interceptor_type_helper(T_Ret (*)(T_Args...));
+}
+
+template<typename T, typename T_Sbx>
+class sandbox_callback
+{
+ KEEP_CLASSES_FRIENDLY
+
+private:
+ rlbox_sandbox<T_Sbx>* sandbox;
+
+ using T_Callback =
+ decltype(callback_detail::callback_type_helper<T_Sbx>(std::declval<T>()));
+ T_Callback callback;
+
+ // The interceptor is the function that runs between the sandbox invoking the
+ // callback and the actual callback running The interceptor is responsible for
+ // wrapping and converting callback arguments, returns etc. to their
+ // appropriate representations
+ using T_Interceptor = decltype(
+ callback_detail::interceptor_type_helper<T_Sbx>(std::declval<T>()));
+ T_Interceptor callback_interceptor;
+
+ // The trampoline is the internal sandbox representation of the callback
+ // Depending on the sandbox type, this could be the callback pointer directly
+ // or a trampoline function that gates exits from the sandbox.
+ using T_Trampoline = detail::convert_to_sandbox_equivalent_t<T, T_Sbx>;
+ T_Trampoline callback_trampoline;
+
+ // The unique key representing the callback to pass to unregister_callback on
+ // destruction
+ void* key;
+
+ inline void move_obj(sandbox_callback&& other)
+ {
+ sandbox = other.sandbox;
+ callback = other.callback;
+ callback_interceptor = other.callback_interceptor;
+ callback_trampoline = other.callback_trampoline;
+ key = other.key;
+ other.sandbox = nullptr;
+ other.callback = nullptr;
+ other.callback_interceptor = nullptr;
+ other.callback_trampoline = 0;
+ other.key = nullptr;
+ }
+
+ template<typename T_Ret, typename... T_Args>
+ inline void unregister_helper(T_Ret (*)(T_Args...))
+ {
+ if (callback != nullptr) {
+ // Don't need to worry about race between unregister and move as
+ // 1) this will not happen in a correctly written program
+ // 2) if this does happen, the worst that can happen is an invocation of a
+ // null function pointer, which causes a crash that cannot be exploited
+ // for RCE
+ sandbox->template unregister_callback<T_Ret, T_Args...>(key);
+ sandbox = nullptr;
+ callback = nullptr;
+ callback_interceptor = nullptr;
+ callback_trampoline = 0;
+ key = nullptr;
+ }
+ }
+
+ inline T_Callback get_raw_value() const noexcept { return callback; }
+ inline T_Trampoline get_raw_sandbox_value() const noexcept
+ {
+ return callback_trampoline;
+ }
+
+ // Keep constructor private as only rlbox_sandbox should be able to create
+ // this object
+ sandbox_callback(rlbox_sandbox<T_Sbx>* p_sandbox,
+ T_Callback p_callback,
+ T_Interceptor p_callback_interceptor,
+ T_Trampoline p_callback_trampoline,
+ void* p_key)
+ : sandbox(p_sandbox)
+ , callback(p_callback)
+ , callback_interceptor(p_callback_interceptor)
+ , callback_trampoline(p_callback_trampoline)
+ , key(p_key)
+ {
+ detail::dynamic_check(sandbox != nullptr,
+ "Unexpected null sandbox when creating a callback");
+ }
+
+public:
+ sandbox_callback()
+ : sandbox(nullptr)
+ , callback(nullptr)
+ , callback_interceptor(nullptr)
+ , callback_trampoline(0)
+ , key(nullptr)
+ {}
+
+ sandbox_callback(sandbox_callback&& other)
+ {
+ move_obj(std::forward<sandbox_callback>(other));
+ }
+
+ inline sandbox_callback& operator=(sandbox_callback&& other)
+ {
+ if (this != &other) {
+ move_obj(std::forward<sandbox_callback>(other));
+ }
+ return *this;
+ }
+
+ void unregister()
+ {
+ T dummy = nullptr;
+ unregister_helper(dummy);
+ }
+
+ ~sandbox_callback() { unregister(); }
+
+ /**
+ * @brief Check if callback is _not_ registered.
+ */
+ inline bool is_unregistered() const noexcept
+ {
+ return get_raw_value() == nullptr;
+ }
+
+ /**
+ * @brief Unwrap a callback without verification. This is an unsafe operation
+ * and should be used with care.
+ */
+ inline auto UNSAFE_unverified() const noexcept { return get_raw_value(); }
+ /**
+ * @brief Like UNSAFE_unverified, but get the underlying sandbox
+ * representation.
+ *
+ * @param sandbox Reference to sandbox.
+ */
+ inline auto UNSAFE_sandboxed(rlbox_sandbox<T_Sbx>& sandbox) const noexcept
+ {
+ RLBOX_UNUSED(sandbox);
+ return get_raw_sandbox_value();
+ }
+};
+
+template<typename T, typename T_Sbx>
+class app_pointer
+{
+ KEEP_CLASSES_FRIENDLY
+
+private:
+ app_pointer_map<typename T_Sbx::T_PointerType>* map;
+ typename T_Sbx::T_PointerType idx;
+ T idx_unsandboxed;
+
+ inline void move_obj(app_pointer&& other)
+ {
+ map = other.map;
+ idx = other.idx;
+ idx_unsandboxed = other.idx_unsandboxed;
+ other.map = nullptr;
+ other.idx = 0;
+ other.idx_unsandboxed = nullptr;
+ }
+
+ inline T get_raw_value() const noexcept
+ {
+ return to_tainted().get_raw_value();
+ }
+ inline typename T_Sbx::T_PointerType get_raw_sandbox_value() const noexcept
+ {
+ return idx;
+ }
+
+ app_pointer(app_pointer_map<typename T_Sbx::T_PointerType>* a_map,
+ typename T_Sbx::T_PointerType a_idx,
+ T a_idx_unsandboxed)
+ : map(a_map)
+ , idx(a_idx)
+ , idx_unsandboxed(a_idx_unsandboxed)
+ {}
+
+public:
+ app_pointer()
+ : map(nullptr)
+ , idx(0)
+ , idx_unsandboxed(0)
+ {}
+
+ ~app_pointer() { unregister(); }
+
+ app_pointer(app_pointer&& other)
+ {
+ move_obj(std::forward<app_pointer>(other));
+ }
+
+ inline app_pointer& operator=(app_pointer&& other)
+ {
+ if (this != &other) {
+ move_obj(std::forward<app_pointer>(other));
+ }
+ return *this;
+ }
+
+ void unregister()
+ {
+ if (idx != 0) {
+ map->remove_app_ptr(idx);
+ map = nullptr;
+ idx = 0;
+ idx_unsandboxed = nullptr;
+ }
+ }
+
+ tainted<T, T_Sbx> to_tainted()
+ {
+ return tainted<T, T_Sbx>::internal_factory(
+ reinterpret_cast<T>(idx_unsandboxed));
+ }
+
+ /**
+ * @brief Check if app pointer is _not_ registered.
+ */
+ inline bool is_unregistered() const noexcept { return idx == 0; }
+
+ /**
+ * @brief Unwrap app_pointer without verification. This is an unsafe operation
+ * and should be used with care.
+ */
+ inline auto UNSAFE_unverified() const noexcept { return get_raw_value(); }
+ /**
+ * @brief Like UNSAFE_unverified, but get the underlying sandbox
+ * representation.
+ *
+ * @param sandbox Reference to sandbox.
+ */
+ inline auto UNSAFE_sandboxed(rlbox_sandbox<T_Sbx>& sandbox) const noexcept
+ {
+ RLBOX_UNUSED(sandbox);
+ return get_raw_sandbox_value();
+ }
+};
+
+/**
+ * @brief Tainted boolean value that serves as a "hint" and not a definite
+ * answer. Comparisons with a tainted_volatile return such hints. They are
+ * not `tainted<bool>` values because a compromised sandbox can modify
+ * tainted_volatile data at any time.
+ */
+class tainted_boolean_hint
+{
+private:
+ bool val;
+
+public:
+ tainted_boolean_hint(bool init)
+ : val(init)
+ {}
+ tainted_boolean_hint(const tainted_boolean_hint&) = default;
+ inline tainted_boolean_hint& operator=(bool rhs)
+ {
+ val = rhs;
+ return *this;
+ }
+ inline tainted_boolean_hint operator!() const
+ {
+ return tainted_boolean_hint(!val);
+ }
+ template<size_t N>
+ inline bool unverified_safe_because(const char (&reason)[N]) const
+ {
+ (void)reason; /* unused */
+ return val;
+ }
+ inline bool UNSAFE_unverified() const { return val; }
+ inline auto INTERNAL_unverified_safe() const { return UNSAFE_unverified(); }
+
+ // Add a template parameter to make sure the assert only fires when called
+ template<typename T = void>
+ inline bool copy_and_verify(...) const
+ {
+ rlbox_detail_static_fail_because(
+ detail::true_v<T>,
+ "You can't call copy_and_verify on this value, as this is a result of a "
+ "comparison with memory accessible by the sandbox. \n"
+ "The sandbox could unexpectedly change the value leading to "
+ "time-of-check-time-of-use attacks. \n"
+ "You can avoid this by making a local copy of the data."
+ "For example, if your original code, looked like \n"
+ "if ((tainted_ptr->member == 5).copy_and_verify(...)) { ... } \n\n"
+ "Change this to \n\n"
+ "tainted<int> val = tainted_ptr->member\n"
+ "if ((val == 5).copy_and_verify(...)) { ... } \n\n"
+ "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox) {...} \n\n"
+ "Alternately, if you are sure your code is safe you can use the "
+ "unverified_safe_because API to remove tainting\n");
+
+ // this is never executed, but we need it for the function to type-check
+ return false;
+ }
+};
+
+/**
+ * @brief Tainted integer value that serves as a "hint" and not a definite
+ * answer. Comparisons with a tainted_volatile return such hints. They are
+ * not `tainted<int>` values because a compromised sandbox can modify
+ * tainted_volatile data at any time.
+ */
+class tainted_int_hint
+{
+private:
+ int val;
+
+public:
+ tainted_int_hint(int init)
+ : val(init)
+ {}
+ tainted_int_hint(const tainted_int_hint&) = default;
+ inline tainted_int_hint& operator=(int rhs)
+ {
+ val = rhs;
+ return *this;
+ }
+ inline tainted_boolean_hint operator!() const
+ {
+ return tainted_boolean_hint(!val);
+ }
+ template<size_t N>
+ inline int unverified_safe_because(const char (&reason)[N]) const
+ {
+ (void)reason; /* unused */
+ return val;
+ }
+ inline int UNSAFE_unverified() const { return val; }
+ inline auto INTERNAL_unverified_safe() const { return UNSAFE_unverified(); }
+
+ // Add a template parameter to make sure the assert only fires when called
+ template<typename T = void>
+ inline int copy_and_verify(...) const
+ {
+ rlbox_detail_static_fail_because(
+ detail::true_v<T>,
+ "You can't call copy_and_verify on this value, as this is a result of a "
+ "comparison with memory accessible by the sandbox. \n"
+ "The sandbox could unexpectedly change the value leading to "
+ "time-of-check-time-of-use attacks. \n"
+ "You can avoid this by making a local copy of the data."
+ "For example, if your original code, looked like \n"
+ "if ((tainted_ptr->member == 5).copy_and_verify(...)) { ... } \n\n"
+ "Change this to \n\n"
+ "tainted<int> val = tainted_ptr->member\n"
+ "if ((val == 5).copy_and_verify(...)) { ... } \n\n"
+ "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox) {...} \n\n"
+ "Alternately, if you are sure your code is safe you can use the "
+ "unverified_safe_because API to remove tainting\n");
+
+ // this is never executed, but we need it for the function to type-check
+ return 0;
+ }
+};
+
+}