2 // IWYU pragma: private, include "rlbox.hpp"
3 // IWYU pragma: friend "rlbox_.*\.hpp"
7 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
14 #ifndef RLBOX_USE_CUSTOM_SHARED_LOCK
15 # include <shared_mutex>
17 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
22 #include <type_traits>
26 #include "rlbox_conversion.hpp"
27 #include "rlbox_helpers.hpp"
28 #include "rlbox_stdlib_polyfill.hpp"
29 #include "rlbox_struct_support.hpp"
30 #include "rlbox_type_traits.hpp"
31 #include "rlbox_wrapper_traits.hpp"
33 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
34 using namespace std::chrono
;
39 namespace convert_fn_ptr_to_sandbox_equivalent_detail
{
40 template<typename T
, typename T_Sbx
>
41 using conv
= ::rlbox::detail::convert_to_sandbox_equivalent_t
<T
, T_Sbx
>;
43 template<typename T_Ret
, typename
... T_Args
>
44 using T_Func
= T_Ret (*)(T_Args
...);
46 template<typename T_Sbx
, typename T_Ret
, typename
... T_Args
>
47 T_Func
<conv
<T_Ret
, T_Sbx
>, conv
<T_Args
, T_Sbx
>...> helper(
48 T_Ret (*)(T_Args
...));
51 #if defined(RLBOX_MEASURE_TRANSITION_TIMES) || \
52 defined(RLBOX_TRANSITION_ACTION_OUT) || defined(RLBOX_TRANSITION_ACTION_IN)
53 enum class rlbox_transition
59 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
60 struct rlbox_transition_timing
62 rlbox_transition invoke
;
67 std::string
to_string()
69 std::ostringstream ret
;
70 if (invoke
== rlbox_transition::INVOKE
) {
73 ret
<< "Callback " << ptr
;
75 ret
<< " : " << time
<< "\n";
82 #ifndef RLBOX_SINGLE_THREADED_INVOCATIONS
84 "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."
88 * @brief Encapsulation for sandboxes.
90 * @tparam T_Sbx Type of sandbox. For the null sandbox this is
91 * `rlbox_noop_sandbox`
93 template<typename T_Sbx
>
94 class rlbox_sandbox
: protected T_Sbx
99 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
100 std::vector
<rlbox_transition_timing
> transition_times
;
103 static inline RLBOX_SHARED_LOCK(sandbox_list_lock
);
104 // The actual type of the vector is std::vector<rlbox_sandbox<T_Sbx>*>
105 // However clang 5, 6 have bugs where compilation seg-faults on this type
106 // So we just use this std::vector<void*>
107 static inline std::vector
<void*> sandbox_list
;
109 RLBOX_SHARED_LOCK(func_ptr_cache_lock
);
110 std::map
<std::string
, void*> func_ptr_map
;
112 app_pointer_map
<typename
T_Sbx::T_PointerType
> app_ptr_map
;
114 // This variable tracks of the sandbox has already been created/destroyed.
115 // APIs in this class should be called only when the sandbox is created.
116 // However, it is expensive to check in APIs such as invoke or in the callback
117 // interceptor. What's more, there could be time of check time of use issues
118 // in the checks as well.
119 // In general, we leave it up to the user to ensure these APIs are never
120 // called prior to sandbox construction or after destruction. We perform some
121 // conservative sanity checks, where they would not add too much overhead.
122 enum class Sandbox_Status
129 std::atomic
<Sandbox_Status
> sandbox_created
= Sandbox_Status::NOT_CREATED
;
131 std::mutex callback_lock
;
132 std::vector
<void*> callback_keys
;
134 void* transition_state
= nullptr;
137 using convert_fn_ptr_to_sandbox_equivalent_t
= decltype(
138 ::rlbox::convert_fn_ptr_to_sandbox_equivalent_detail::helper
<T_Sbx
>(
142 inline constexpr void check_invoke_param_type_is_ok()
144 using T_NoRef
= std::remove_reference_t
<T
>;
146 if_constexpr_named(cond1
, detail::rlbox_is_wrapper_v
<T_NoRef
>)
150 !std::is_same_v
<T_Sbx
, detail::rlbox_get_wrapper_sandbox_t
<T_NoRef
>>)
152 rlbox_detail_static_fail_because(
154 "Mixing tainted data from a different sandbox types. This could "
155 "happen due to couple of different reasons.\n"
156 "1. You are using 2 sandbox types for example'rlbox_noop_sandbox' "
157 "and 'rlbox_lucet_sandbox', and are passing tainted data from one "
158 "sandbox as parameters into a function call to the other sandbox. "
159 "This is not allowed, unwrap the tainted data with copy_and_verify "
160 "or other unwrapping APIs first.\n"
161 "2. You have inadvertantly forgotten to set/remove "
162 "RLBOX_USE_STATIC_CALLS depending on the sandbox type. Some sandbox "
163 "types like rlbox_noop_sandbox require this to be set to a given "
164 "value, while other types like rlbox_lucet_sandbox, require this not "
168 else if_constexpr_named(cond2
,
169 std::is_null_pointer_v
<T_NoRef
> ||
170 detail::is_fundamental_or_enum_v
<T_NoRef
>)
174 constexpr auto unknownCase
= !(cond1
|| cond2
);
175 rlbox_detail_static_fail_because(
177 "Arguments to a sandbox function call should be primitives or wrapped "
178 "types like tainted, callbacks etc.");
183 inline auto invoke_process_param(T
&& param
)
185 check_invoke_param_type_is_ok
<T
>();
187 using T_NoRef
= std::remove_reference_t
<T
>;
189 if constexpr (detail::rlbox_is_tainted_opaque_v
<T_NoRef
>) {
190 auto ret
= from_opaque(param
);
191 return ret
.UNSAFE_sandboxed(*this);
192 } else if constexpr (detail::rlbox_is_wrapper_v
<T_NoRef
>) {
193 return param
.UNSAFE_sandboxed(*this);
194 } else if constexpr (std::is_null_pointer_v
<T_NoRef
>) {
195 tainted
<void*, T_Sbx
> ret
= nullptr;
196 return ret
.UNSAFE_sandboxed(*this);
197 } else if constexpr (detail::is_fundamental_or_enum_v
<T_NoRef
>) {
198 // For unwrapped primitives, assign to a tainted var and then unwrap so
199 // that we adjust for machine model
200 tainted
<T_NoRef
, T_Sbx
> ret
= param
;
201 return ret
.UNSAFE_sandboxed(*this);
203 rlbox_detail_static_fail_because(
204 detail::true_v
<T_NoRef
>,
205 "Only tainted types, callbacks or primitive values such as ints can be "
206 "passed as parameters.\n"
207 "To make a parameter tainted, try moving the allocation into the "
209 "If the parameter is a callback, try registering the callback via the "
210 "register_callback API.");
214 template<typename T
, typename T_Arg
>
215 inline tainted
<T
, T_Sbx
> sandbox_callback_intercept_convert_param(
216 rlbox_sandbox
<T_Sbx
>& sandbox
,
219 tainted
<T
, T_Sbx
> ret
;
220 using namespace detail
;
222 adjust_type_direction::TO_APPLICATION
,
223 adjust_type_context::SANDBOX
>(
224 ret
.get_raw_value_ref(),
226 nullptr /* example_unsandboxed_ptr */,
231 template<typename T_Ret
, typename
... T_Args
>
232 static detail::convert_to_sandbox_equivalent_t
<T_Ret
, T_Sbx
>
233 sandbox_callback_interceptor(
234 detail::convert_to_sandbox_equivalent_t
<T_Args
, T_Sbx
>... args
)
236 std::pair
<T_Sbx
*, void*> context
=
237 T_Sbx::impl_get_executed_callback_sandbox_and_key();
238 auto& sandbox
= *(reinterpret_cast<rlbox_sandbox
<T_Sbx
>*>(context
.first
));
239 auto key
= context
.second
;
242 std::conditional_t
<std::is_void_v
<T_Ret
>, void, tainted
<T_Ret
, T_Sbx
>>;
244 T_Func_Ret (*)(rlbox_sandbox
<T_Sbx
>&, tainted
<T_Args
, T_Sbx
>...);
245 auto target_fn_ptr
= reinterpret_cast<T_Func
>(key
);
247 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
248 high_resolution_clock::time_point enter_time
= high_resolution_clock::now();
249 auto on_exit
= rlbox::detail::make_scope_exit([&] {
250 auto exit_time
= high_resolution_clock::now();
251 int64_t ns
= duration_cast
<nanoseconds
>(exit_time
- enter_time
).count();
252 sandbox
.transition_times
.push_back(
253 rlbox_transition_timing
{ rlbox_transition::CALLBACK
,
254 nullptr /* func_name */,
259 #ifdef RLBOX_TRANSITION_ACTION_OUT
260 RLBOX_TRANSITION_ACTION_OUT(rlbox_transition::CALLBACK
,
261 nullptr /* func_name */,
263 sandbox
.transition_state
);
265 #ifdef RLBOX_TRANSITION_ACTION_IN
266 auto on_exit_transition
= rlbox::detail::make_scope_exit([&] {
267 RLBOX_TRANSITION_ACTION_IN(rlbox_transition::CALLBACK
,
268 nullptr /* func_name */,
270 sandbox
.transition_state
);
273 if constexpr (std::is_void_v
<T_Func_Ret
>) {
276 sandbox
.template sandbox_callback_intercept_convert_param
<T_Args
>(
280 auto tainted_ret
= (*target_fn_ptr
)(
282 sandbox
.template sandbox_callback_intercept_convert_param
<T_Args
>(
285 using namespace detail
;
286 convert_to_sandbox_equivalent_t
<T_Ret
, T_Sbx
> ret
;
288 adjust_type_direction::TO_SANDBOX
,
289 adjust_type_context::SANDBOX
>(
291 tainted_ret
.get_raw_value_ref(),
292 nullptr /* example_unsandboxed_ptr */,
299 * @brief Unregister a callback function and disallow the sandbox from
300 * calling this function henceforth.
302 template<typename T_Ret
, typename
... T_Args
>
303 inline void unregister_callback(void* key
)
305 // Silently swallowing the failure is better here as RAII types may try to
306 // cleanup callbacks after sandbox destruction
307 if (sandbox_created
.load() != Sandbox_Status::CREATED
) {
311 this->template impl_unregister_callback
<
312 detail::convert_to_sandbox_equivalent_t
<T_Ret
, T_Sbx
>,
313 detail::convert_to_sandbox_equivalent_t
<T_Args
, T_Sbx
>...>(key
);
315 std::lock_guard
<std::mutex
> lock(callback_lock
);
316 auto el_ref
= std::find(callback_keys
.begin(), callback_keys
.end(), key
);
317 detail::dynamic_check(
318 el_ref
!= callback_keys
.end(),
319 "Unexpected state. Unregistering a callback that was never registered.");
320 callback_keys
.erase(el_ref
);
323 static T_Sbx
* find_sandbox_from_example(const void* example_sandbox_ptr
)
325 detail::dynamic_check(
326 example_sandbox_ptr
!= nullptr,
327 "Internal error: received a null example pointer. Please file a bug.");
329 RLBOX_ACQUIRE_SHARED_GUARD(lock
, sandbox_list_lock
);
330 for (auto sandbox_v
: sandbox_list
) {
331 auto sandbox
= reinterpret_cast<rlbox_sandbox
<T_Sbx
>*>(sandbox_v
);
332 if (sandbox
->is_pointer_in_sandbox_memory(example_sandbox_ptr
)) {
340 template<typename
... T_Args
>
341 static auto impl_create_sandbox_helper(rlbox_sandbox
<T_Sbx
>* this_ptr
,
344 return this_ptr
->impl_create_sandbox(std::forward
<T_Args
>(args
)...);
349 * @brief Unused member that allows the calling code to save data in a
350 * "per-sandbox" storage. This can be useful to save context which is used
353 void* sandbox_storage
;
355 /***** Function to adjust for custom machine models *****/
358 using convert_to_sandbox_equivalent_nonclass_t
=
359 detail::convert_base_types_t
<T
,
360 typename
T_Sbx::T_ShortType
,
361 typename
T_Sbx::T_IntType
,
362 typename
T_Sbx::T_LongType
,
363 typename
T_Sbx::T_LongLongType
,
364 typename
T_Sbx::T_PointerType
>;
366 T_Sbx
* get_sandbox_impl() { return this; }
369 * @brief Create a new sandbox.
371 * @tparam T_Args Arguments passed to the underlying sandbox
372 * implementation. For the null sandbox, no arguments are necessary.
374 template<typename
... T_Args
>
375 inline bool create_sandbox(T_Args
... args
)
377 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
378 // Warm up the timer. The first call is always slow (at least on the test
380 for (int i
= 0; i
< 10; i
++) {
381 auto val
= high_resolution_clock::now();
385 auto expected
= Sandbox_Status::NOT_CREATED
;
386 bool success
= sandbox_created
.compare_exchange_strong(
387 expected
, Sandbox_Status::INITIALIZING
/* desired */);
388 detail::dynamic_check(
390 "create_sandbox called when sandbox already created/is being "
391 "created concurrently");
393 using T_Result
= rlbox::detail::polyfill::invoke_result_t
<
394 decltype(impl_create_sandbox_helper
<T_Args
...>),
399 if constexpr (std::is_same_v
<T_Result
, void>) {
400 this->impl_create_sandbox(std::forward
<T_Args
>(args
)...);
401 } else if constexpr (std::is_same_v
<T_Result
, bool>) {
402 created
= this->impl_create_sandbox(std::forward
<T_Args
>(args
)...);
404 rlbox_detail_static_fail_because(
405 (!std::is_same_v
<T_Result
, void> && !std::is_same_v
<T_Result
, bool>),
406 "Expected impl_create_sandbox to return void or a boolean");
410 sandbox_created
.store(Sandbox_Status::CREATED
);
411 RLBOX_ACQUIRE_UNIQUE_GUARD(lock
, sandbox_list_lock
);
412 sandbox_list
.push_back(this);
419 * @brief Destroy sandbox and reclaim any memory.
421 inline auto destroy_sandbox()
423 auto expected
= Sandbox_Status::CREATED
;
424 bool success
= sandbox_created
.compare_exchange_strong(
425 expected
, Sandbox_Status::CLEANING_UP
/* desired */);
427 detail::dynamic_check(
429 "destroy_sandbox called without sandbox creation/is being "
430 "destroyed concurrently");
433 RLBOX_ACQUIRE_UNIQUE_GUARD(lock
, sandbox_list_lock
);
434 auto el_ref
= std::find(sandbox_list
.begin(), sandbox_list
.end(), this);
435 detail::dynamic_check(
436 el_ref
!= sandbox_list
.end(),
437 "Unexpected state. Destroying a sandbox that was never initialized.");
438 sandbox_list
.erase(el_ref
);
441 sandbox_created
.store(Sandbox_Status::NOT_CREATED
);
442 return this->impl_destroy_sandbox();
446 inline T
get_unsandboxed_pointer(
447 convert_to_sandbox_equivalent_nonclass_t
<T
> p
) const
449 static_assert(std::is_pointer_v
<T
>);
453 auto ret
= this->template impl_get_unsandboxed_pointer
<T
>(p
);
454 return reinterpret_cast<T
>(ret
);
458 inline convert_to_sandbox_equivalent_nonclass_t
<T
> get_sandboxed_pointer(
461 static_assert(std::is_pointer_v
<T
>);
465 return this->template impl_get_sandboxed_pointer
<T
>(p
);
469 static inline T
get_unsandboxed_pointer_no_ctx(
470 convert_to_sandbox_equivalent_nonclass_t
<T
> p
,
471 const void* example_unsandboxed_ptr
)
473 static_assert(std::is_pointer_v
<T
>);
477 auto ret
= T_Sbx::template impl_get_unsandboxed_pointer_no_ctx
<T
>(
478 p
, example_unsandboxed_ptr
, find_sandbox_from_example
);
479 return reinterpret_cast<T
>(ret
);
483 static inline convert_to_sandbox_equivalent_nonclass_t
<T
>
484 get_sandboxed_pointer_no_ctx(const void* p
,
485 const void* example_unsandboxed_ptr
)
487 static_assert(std::is_pointer_v
<T
>);
491 return T_Sbx::template impl_get_sandboxed_pointer_no_ctx
<T
>(
492 p
, example_unsandboxed_ptr
, find_sandbox_from_example
);
496 * @brief Allocate a new pointer that is accessible to both the application
497 * and sandbox. The pointer is allocated in sandbox memory.
499 * @tparam T The type of the pointer you want to create. If T=int, this
500 * would return a pointer to an int.
502 * @return tainted<T*, T_Sbx> Tainted pointer accessible to the application
506 inline tainted
<T
*, T_Sbx
> malloc_in_sandbox()
508 const uint32_t defaultCount
= 1;
509 return malloc_in_sandbox
<T
>(defaultCount
);
513 * @brief Allocate an array that is accessible to both the application
514 * and sandbox. The pointer is allocated in sandbox memory.
516 * @tparam T The type of the array elements you want to create. If T=int, this
517 * would return a pointer to an array of ints.
519 * @param count The number of array elements to allocate.
521 * @return tainted<T*, T_Sbx> Tainted pointer accessible to the application
525 inline tainted
<T
*, T_Sbx
> malloc_in_sandbox(uint32_t count
)
527 // Silently swallowing the failure is better here as RAII types may try to
528 // malloc after sandbox destruction
529 if (sandbox_created
.load() != Sandbox_Status::CREATED
) {
530 return tainted
<T
*, T_Sbx
>::internal_factory(nullptr);
533 detail::dynamic_check(count
!= 0, "Malloc tried to allocate 0 bytes");
534 if constexpr (sizeof(T
) >= std::numeric_limits
<uint32_t>::max()) {
535 rlbox_detail_static_fail_because(sizeof(T
) >=
536 std::numeric_limits
<uint32_t>::max(),
537 "Tried to allocate an object over 4GB.");
539 auto total_size
= static_cast<uint64_t>(sizeof(T
)) * count
;
540 if constexpr (sizeof(size_t) == 4) {
541 // On a 32-bit platform, we need to make sure that total_size is not >=4GB
542 detail::dynamic_check(total_size
< std::numeric_limits
<uint32_t>::max(),
543 "Tried to allocate memory over 4GB");
544 } else if constexpr (sizeof(size_t) != 8) {
545 // Double check we are on a 64-bit platform
546 // Note for static checks we need to have some dependence on T, so adding
548 constexpr bool dummy
= sizeof(T
) >= 0;
549 rlbox_detail_static_fail_because(dummy
&& sizeof(size_t) != 8,
550 "Expected 32 or 64 bit platform.");
552 auto ptr_in_sandbox
= this->impl_malloc_in_sandbox(total_size
);
553 auto ptr
= get_unsandboxed_pointer
<T
*>(ptr_in_sandbox
);
555 return tainted
<T
*, T_Sbx
>(nullptr);
557 detail::dynamic_check(is_pointer_in_sandbox_memory(ptr
),
558 "Malloc returned pointer outside the sandbox memory");
559 auto ptr_end
= reinterpret_cast<uintptr_t>(ptr
+ (count
- 1));
560 detail::dynamic_check(
561 is_in_same_sandbox(ptr
, reinterpret_cast<void*>(ptr_end
)),
562 "Malloc returned a pointer whose range goes beyond sandbox memory");
563 auto cast_ptr
= reinterpret_cast<T
*>(ptr
);
564 return tainted
<T
*, T_Sbx
>::internal_factory(cast_ptr
);
568 * @brief Free the memory referenced by the tainted pointer.
570 * @param ptr Pointer to sandbox memory to free.
573 inline void free_in_sandbox(tainted
<T
*, T_Sbx
> ptr
)
575 // Silently swallowing the failure is better here as RAII types may try to
576 // free after sandbox destruction
577 if (sandbox_created
.load() != Sandbox_Status::CREATED
) {
581 this->impl_free_in_sandbox(ptr
.get_raw_sandbox_value(*this));
585 * @brief Free the memory referenced by a tainted_volatile pointer ref.
587 * @param ptr_ref Pointer reference to sandbox memory to free.
590 inline void free_in_sandbox(tainted_volatile
<T
, T_Sbx
>& ptr_ref
)
592 tainted
<T
, T_Sbx
> ptr
= ptr_ref
;
593 free_in_sandbox(ptr
);
597 * @brief Free the memory referenced by a tainted_opaque pointer.
599 * @param ptr_opaque Opaque pointer to sandbox memory to free.
602 inline void free_in_sandbox(tainted_opaque
<T
, T_Sbx
> ptr_opaque
)
604 tainted
<T
, T_Sbx
> ptr
= from_opaque(ptr_opaque
);
605 free_in_sandbox(ptr
);
609 * @brief Check if two pointers are in the same sandbox.
610 * For the null-sandbox, this always returns true.
612 static inline bool is_in_same_sandbox(const void* p1
, const void* p2
)
614 const size_t num_args
=
615 detail::func_arg_nums_v
<decltype(T_Sbx::impl_is_in_same_sandbox
)>;
616 if constexpr (num_args
== 2) {
617 return T_Sbx::impl_is_in_same_sandbox(p1
, p2
);
619 return T_Sbx::impl_is_in_same_sandbox(p1
, p2
, find_sandbox_from_example
);
624 * @brief Check if the pointer points to this sandbox's memory.
625 * For the null-sandbox, this always returns true.
627 inline bool is_pointer_in_sandbox_memory(const void* p
)
629 return this->impl_is_pointer_in_sandbox_memory(p
);
633 * @brief Check if the pointer points to application memory.
634 * For the null-sandbox, this always returns true.
636 inline bool is_pointer_in_app_memory(const void* p
)
638 return this->impl_is_pointer_in_app_memory(p
);
641 inline size_t get_total_memory() { return this->impl_get_total_memory(); }
643 inline void* get_memory_location()
645 return this->impl_get_memory_location();
648 void* get_transition_state() { return transition_state
; }
650 void set_transition_state(void* new_state
) { transition_state
= new_state
; }
653 * @brief For internal use only.
654 * Grant access of the passed in buffer in to the sandbox instance. Called by
655 * internal APIs only if the underlying sandbox supports
656 * can_grant_deny_access by including the line
658 * using can_grant_deny_access = void;
662 inline tainted
<T
*, T_Sbx
> INTERNAL_grant_access(T
* src
,
666 auto ret
= this->impl_grant_access(src
, num
, success
);
667 return tainted
<T
*, T_Sbx
>::internal_factory(ret
);
671 * @brief For internal use only.
672 * Grant access of the passed in buffer in to the sandbox instance. Called by
673 * internal APIs only if the underlying sandbox supports
674 * can_grant_deny_access by including the line
676 * using can_grant_deny_access = void;
680 inline T
* INTERNAL_deny_access(tainted
<T
*, T_Sbx
> src
,
685 this->impl_deny_access(src
.INTERNAL_unverified_safe(), num
, success
);
689 void* lookup_symbol(const char* func_name
)
692 RLBOX_ACQUIRE_SHARED_GUARD(lock
, func_ptr_cache_lock
);
694 auto func_ptr_ref
= func_ptr_map
.find(func_name
);
695 if (func_ptr_ref
!= func_ptr_map
.end()) {
696 return func_ptr_ref
->second
;
700 void* func_ptr
= this->impl_lookup_symbol(func_name
);
701 RLBOX_ACQUIRE_UNIQUE_GUARD(lock
, func_ptr_cache_lock
);
702 func_ptr_map
[func_name
] = func_ptr
;
706 void* internal_lookup_symbol(const char* func_name
)
709 RLBOX_ACQUIRE_SHARED_GUARD(lock
, func_ptr_cache_lock
);
711 auto func_ptr_ref
= func_ptr_map
.find(func_name
);
712 if (func_ptr_ref
!= func_ptr_map
.end()) {
713 return func_ptr_ref
->second
;
718 if constexpr (rlbox::detail::
719 has_member_using_needs_internal_lookup_symbol_v
<T_Sbx
>) {
720 func_ptr
= this->impl_internal_lookup_symbol(func_name
);
722 func_ptr
= this->impl_lookup_symbol(func_name
);
724 RLBOX_ACQUIRE_UNIQUE_GUARD(lock
, func_ptr_cache_lock
);
725 func_ptr_map
[func_name
] = func_ptr
;
729 // this is an internal function invoked from macros, so it has be public
730 template<typename T
, typename
... T_Args
>
731 inline auto INTERNAL_invoke_with_func_name(const char* func_name
,
734 return INTERNAL_invoke_with_func_ptr
<T
, T_Args
...>(
735 func_name
, lookup_symbol(func_name
), std::forward
<T_Args
>(params
)...);
738 // this is an internal function invoked from macros, so it has be public
739 // Explicitly don't use inline on this, as this adds a lot of instructions
740 // prior to function call. What's more, by not inlining, different function
741 // calls with the same signature can share the same code segments for
742 // sandboxed function execution in the binary
743 template<typename T
, typename
... T_Args
>
744 auto INTERNAL_invoke_with_func_ptr(const char* func_name
,
748 // unused in some paths
749 RLBOX_UNUSED(func_name
);
750 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
751 auto enter_time
= high_resolution_clock::now();
752 auto on_exit
= rlbox::detail::make_scope_exit([&] {
753 auto exit_time
= high_resolution_clock::now();
754 int64_t ns
= duration_cast
<nanoseconds
>(exit_time
- enter_time
).count();
755 transition_times
.push_back(rlbox_transition_timing
{
756 rlbox_transition::INVOKE
, func_name
, func_ptr
, ns
});
759 #ifdef RLBOX_TRANSITION_ACTION_IN
760 RLBOX_TRANSITION_ACTION_IN(
761 rlbox_transition::INVOKE
, func_name
, func_ptr
, transition_state
);
763 #ifdef RLBOX_TRANSITION_ACTION_OUT
764 auto on_exit_transition
= rlbox::detail::make_scope_exit([&] {
765 RLBOX_TRANSITION_ACTION_OUT(
766 rlbox_transition::INVOKE
, func_name
, func_ptr
, transition_state
);
769 (check_invoke_param_type_is_ok
<T_Args
>(), ...);
772 rlbox::detail::polyfill::is_invocable_v
<
774 detail::rlbox_remove_wrapper_t
<std::remove_reference_t
<T_Args
>>...>,
775 "Mismatched arguments types for function");
777 using T_Result
= rlbox::detail::polyfill::invoke_result_t
<
779 detail::rlbox_remove_wrapper_t
<std::remove_reference_t
<T_Args
>>...>;
782 std::remove_pointer_t
<convert_fn_ptr_to_sandbox_equivalent_t
<T
*>>;
784 if constexpr (std::is_void_v
<T_Result
>) {
785 this->template impl_invoke_with_func_ptr
<T
>(
786 reinterpret_cast<T_Converted
*>(func_ptr
),
787 invoke_process_param(params
)...);
790 auto raw_result
= this->template impl_invoke_with_func_ptr
<T
>(
791 reinterpret_cast<T_Converted
*>(func_ptr
),
792 invoke_process_param(params
)...);
793 tainted
<T_Result
, T_Sbx
> wrapped_result
;
794 using namespace detail
;
796 adjust_type_direction::TO_APPLICATION
,
797 adjust_type_context::SANDBOX
>(
798 wrapped_result
.get_raw_value_ref(),
800 nullptr /* example_unsandboxed_ptr */,
801 this /* sandbox_ptr */);
802 return wrapped_result
;
806 // Useful in the porting stage to temporarily allow non tainted pointers to go
807 // through. This will only ever work in the rlbox_noop_sandbox. Any sandbox
808 // that actually enforces isolation will crash here.
809 template<typename T2
>
810 tainted
<T2
, T_Sbx
> UNSAFE_accept_pointer(T2 ptr
)
812 static_assert(std::is_pointer_v
<T2
>,
813 "UNSAFE_accept_pointer expects a pointer param");
814 tainted
<T2
, T_Sbx
> ret
;
815 ret
.assign_raw_pointer(*this, ptr
);
819 template<typename T_Ret
, typename
... T_Args
>
820 using T_Cb_no_wrap
= detail::rlbox_remove_wrapper_t
<T_Ret
>(
821 detail::rlbox_remove_wrapper_t
<T_Args
>...);
823 template<typename T_Ret
>
824 sandbox_callback
<T_Cb_no_wrap
<T_Ret
>*, T_Sbx
> register_callback(T_Ret (*)())
826 rlbox_detail_static_fail_because(
827 detail::true_v
<T_Ret
>,
828 "Modify the callback to change the first parameter to a sandbox. "
829 "For instance if a callback has type\n\n"
830 "int foo() {...}\n\n"
831 "Change this to \n\n"
832 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox) {...}\n");
834 // this is never executed, but we need it for the function to type-check
839 * @brief Expose a callback function to the sandboxed code.
841 * @param func_ptr The callback to expose.
843 * @tparam T_RL Sandbox reference type (first argument).
844 * @tparam T_Ret Return type of callback. Must be tainted or void.
845 * @tparam T_Args Types of remaining callback arguments. Must be tainted.
847 * @return Wrapped callback function pointer that can be passed to the
850 template<typename T_RL
, typename T_Ret
, typename
... T_Args
>
851 sandbox_callback
<T_Cb_no_wrap
<T_Ret
, T_Args
...>*, T_Sbx
> register_callback(
852 T_Ret (*func_ptr
)(T_RL
, T_Args
...))
854 // Some branches don't use the param
855 RLBOX_UNUSED(func_ptr
);
857 if_constexpr_named(cond1
, !std::is_same_v
<T_RL
, rlbox_sandbox
<T_Sbx
>&>)
859 rlbox_detail_static_fail_because(
861 "Modify the callback to change the first parameter to a sandbox. "
862 "For instance if a callback has type\n\n"
863 "int foo(int a, int b) {...}\n\n"
864 "Change this to \n\n"
865 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox, "
866 "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
868 else if_constexpr_named(
869 cond2
, !(detail::rlbox_is_tainted_or_opaque_v
<T_Args
> && ...))
871 rlbox_detail_static_fail_because(
873 "Change all arguments to the callback have to be tainted or "
875 "For instance if a callback has type\n\n"
876 "int foo(int a, int b) {...}\n\n"
877 "Change this to \n\n"
878 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox, "
879 "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
881 else if_constexpr_named(
882 cond3
, (std::is_array_v
<detail::rlbox_remove_wrapper_t
<T_Args
>> || ...))
884 rlbox_detail_static_fail_because(
886 "Change all static array arguments to the callback to be pointers. "
887 "For instance if a callback has type\n\n"
888 "int foo(int a[4]) {...}\n\n"
889 "Change this to \n\n"
890 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox, "
891 "tainted<int*, T_Sbx> a) {...}\n");
893 else if_constexpr_named(
895 !(std::is_void_v
<T_Ret
> || detail::rlbox_is_tainted_or_opaque_v
<T_Ret
>))
897 rlbox_detail_static_fail_because(
899 "Change the callback return type to be tainted or tainted_opaque if it "
901 "For instance if a callback has type\n\n"
902 "int foo(int a, int b) {...}\n\n"
903 "Change this to \n\n"
904 "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox, "
905 "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
909 detail::dynamic_check(
910 sandbox_created
.load() == Sandbox_Status::CREATED
,
911 "register_callback called without sandbox creation");
913 // Need unique key for each callback we register - just use the func addr
914 void* unique_key
= reinterpret_cast<void*>(func_ptr
);
916 // Make sure that the user hasn't previously registered this function...
917 // If they have, we would returning 2 owning types (sandbox_callback) to
918 // the same callback which would be bad
920 std::lock_guard
<std::mutex
> lock(callback_lock
);
922 std::find(callback_keys
.begin(), callback_keys
.end(), unique_key
) !=
924 detail::dynamic_check(
925 !exists
, "You have previously already registered this callback.");
926 callback_keys
.push_back(unique_key
);
929 auto callback_interceptor
=
930 sandbox_callback_interceptor
<detail::rlbox_remove_wrapper_t
<T_Ret
>,
931 detail::rlbox_remove_wrapper_t
<T_Args
>...>;
933 auto callback_trampoline
= this->template impl_register_callback
<
934 detail::convert_to_sandbox_equivalent_t
<
935 detail::rlbox_remove_wrapper_t
<T_Ret
>,
937 detail::convert_to_sandbox_equivalent_t
<
938 detail::rlbox_remove_wrapper_t
<T_Args
>,
939 T_Sbx
>...>(unique_key
, reinterpret_cast<void*>(callback_interceptor
));
941 auto tainted_func_ptr
= reinterpret_cast<
942 detail::rlbox_tainted_opaque_to_tainted_t
<T_Ret
, T_Sbx
> (*)(
943 T_RL
, detail::rlbox_tainted_opaque_to_tainted_t
<T_Args
, T_Sbx
>...)>(
944 reinterpret_cast<void*>(func_ptr
));
946 auto ret
= sandbox_callback
<T_Cb_no_wrap
<T_Ret
, T_Args
...>*, T_Sbx
>(
949 callback_interceptor
,
956 // this is an internal function invoked from macros, so it has be public
958 inline tainted
<T
*, T_Sbx
> INTERNAL_get_sandbox_function_name(
959 const char* func_name
)
961 return INTERNAL_get_sandbox_function_ptr
<T
>(
962 internal_lookup_symbol(func_name
));
965 // this is an internal function invoked from macros, so it has be public
967 inline tainted
<T
*, T_Sbx
> INTERNAL_get_sandbox_function_ptr(void* func_ptr
)
969 return tainted
<T
*, T_Sbx
>::internal_factory(reinterpret_cast<T
*>(func_ptr
));
973 * @brief Create a "fake" pointer referring to a location in the application
976 * @param ptr The pointer to refer to
978 * @return The app_pointer object that refers to this location.
981 app_pointer
<T
*, T_Sbx
> get_app_pointer(T
* ptr
)
983 auto max_ptr
= (typename
T_Sbx::T_PointerType
)(get_total_memory() - 1);
984 auto idx
= app_ptr_map
.get_app_pointer_idx((void*)ptr
, max_ptr
);
985 auto idx_as_ptr
= this->template impl_get_unsandboxed_pointer
<T
>(idx
);
986 // Right now we simply assume that any integer can be converted to a valid
987 // pointer in the sandbox This may not be true for some sandboxing mechanism
988 // plugins in the future In this case, we will have to come up with
989 // something more clever to construct indexes that look like valid pointers
990 // Add a check for now to make sure things work fine
991 detail::dynamic_check(is_pointer_in_sandbox_memory(idx_as_ptr
),
992 "App pointers are not currently supported for this "
993 "rlbox sandbox plugin. Please file a bug.");
994 auto ret
= app_pointer
<T
*, T_Sbx
>(
995 &app_ptr_map
, idx
, reinterpret_cast<T
*>(idx_as_ptr
));
1000 * @brief The mirror of get_app_pointer. Take a tainted pointer which is
1001 * actually an app_pointer, and get the application location being pointed to
1003 * @param tainted_ptr The tainted pointer that is actually an app_pointer
1005 * @return The original location being referred to by the app_ptr
1007 template<typename T
>
1008 T
* lookup_app_ptr(tainted
<T
*, T_Sbx
> tainted_ptr
)
1010 auto idx
= tainted_ptr
.get_raw_sandbox_value(*this);
1011 void* ret
= app_ptr_map
.lookup_index(idx
);
1012 return reinterpret_cast<T
*>(ret
);
1015 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
1016 inline std::vector
<rlbox_transition_timing
>&
1017 process_and_get_transition_times()
1019 return transition_times
;
1021 inline int64_t get_total_ns_time_in_sandbox_and_transitions()
1024 for (auto& transition_time
: transition_times
) {
1025 if (transition_time
.invoke
== rlbox_transition::INVOKE
) {
1026 ret
+= transition_time
.time
;
1028 ret
-= transition_time
.time
;
1033 inline void clear_transition_times() { transition_times
.clear(); }
1037 #if defined(__clang__)
1038 # pragma clang diagnostic push
1039 # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
1040 #elif defined(__GNUC__) || defined(__GNUG__)
1041 // Can't turn off the variadic macro warning emitted from -pedantic so use a
1042 // hack to stop GCC emitting warnings for the reminder of this file
1043 # pragma GCC system_header
1044 #elif defined(_MSC_VER)
1045 // Doesn't seem to emit the warning
1047 // Don't know the compiler... just let it go through
1051 * @def invoke_sandbox_function
1052 * @brief Call sandbox function.
1054 * @param func_name The sandboxed library function to call.
1055 * @param ... Arguments to function should be simple or tainted values.
1056 * @return Tainted value or void.
1058 #ifdef RLBOX_USE_STATIC_CALLS
1060 # define sandbox_lookup_symbol_helper(prefix, func_name) prefix(func_name)
1062 # define invoke_sandbox_function(func_name, ...) \
1063 template INTERNAL_invoke_with_func_ptr<decltype(func_name)>( \
1065 sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name), \
1068 # define get_sandbox_function_address(func_name) \
1069 template INTERNAL_get_sandbox_function_ptr<decltype(func_name)>( \
1070 sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name))
1074 # define invoke_sandbox_function(func_name, ...) \
1075 template INTERNAL_invoke_with_func_name<decltype(func_name)>( \
1076 #func_name, ##__VA_ARGS__)
1078 # define get_sandbox_function_address(func_name) \
1079 template INTERNAL_get_sandbox_function_name<decltype(func_name)>(#func_name)
1083 #define sandbox_invoke(sandbox, func_name, ...) \
1084 (sandbox).invoke_sandbox_function(func_name, ##__VA_ARGS__)
1086 #define sandbox_function_address(sandbox, func_name) \
1087 (sandbox).get_sandbox_function_address(func_name)
1089 #if defined(__clang__)
1090 # pragma clang diagnostic pop