Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / third_party / rlbox / include / rlbox.hpp
blob48361d7a2bfbd82ce546f06a55937790f5671473
1 #pragma once
3 #include <array>
4 #include <cstring>
5 #include <memory>
6 #include <type_traits>
7 #include <utility>
9 #include "rlbox_app_pointer.hpp"
10 #include "rlbox_conversion.hpp"
11 #include "rlbox_helpers.hpp"
12 #include "rlbox_policy_types.hpp"
13 #include "rlbox_range.hpp"
14 #include "rlbox_sandbox.hpp"
15 #include "rlbox_stdlib.hpp"
16 #include "rlbox_struct_support.hpp"
17 #include "rlbox_type_traits.hpp"
18 #include "rlbox_types.hpp"
19 #include "rlbox_unwrap.hpp"
20 #include "rlbox_wrapper_traits.hpp"
22 namespace rlbox {
24 template<template<typename, typename> typename T_Wrap,
25 typename T,
26 typename T_Sbx>
27 class tainted_base_impl
29 KEEP_CLASSES_FRIENDLY
30 KEEP_CAST_FRIENDLY
32 public:
33 inline auto& impl() { return *static_cast<T_Wrap<T, T_Sbx>*>(this); }
34 inline auto& impl() const
36 return *static_cast<const T_Wrap<T, T_Sbx>*>(this);
39 /**
40 * @brief Unwrap a tainted value without verification. This is an unsafe
41 * operation and should be used with care.
43 inline auto UNSAFE_unverified() const { return impl().get_raw_value(); }
44 /**
45 * @brief Like UNSAFE_unverified, but get the underlying sandbox
46 * representation.
48 * @param sandbox Reference to sandbox.
50 * For the Wasm-based sandbox, this function additionally validates the
51 * unwrapped value against the machine model of the sandbox (LP32).
53 inline auto UNSAFE_sandboxed(rlbox_sandbox<T_Sbx>& sandbox) const
55 return impl().get_raw_sandbox_value(sandbox);
58 /**
59 * @brief Unwrap a tainted value without verification. This function should
60 * be used when unwrapping is safe.
62 * @param reason An explanation why the unverified unwrapping is safe.
64 template<size_t N>
65 inline auto unverified_safe_because(const char (&reason)[N]) const
67 RLBOX_UNUSED(reason);
68 static_assert(!std::is_pointer_v<T>,
69 "unverified_safe_because does not support pointers. Use "
70 "unverified_safe_pointer_because.");
71 return UNSAFE_unverified();
74 template<size_t N>
75 inline auto unverified_safe_pointer_because(size_t count,
76 const char (&reason)[N]) const
78 RLBOX_UNUSED(reason);
80 static_assert(std::is_pointer_v<T>, "Expected pointer type");
81 using T_Pointed = std::remove_pointer_t<T>;
82 if_constexpr_named(cond1, std::is_pointer_v<T_Pointed>)
84 rlbox_detail_static_fail_because(
85 cond1,
86 "There is no way to use unverified_safe_pointer_because for "
87 "'pointers to pointers' safely. Use copy_and_verify instead.");
88 return nullptr;
91 auto ret = UNSAFE_unverified();
92 if (ret != nullptr) {
93 size_t bytes = sizeof(T) * count;
94 detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(ret, bytes);
96 return ret;
99 inline auto INTERNAL_unverified_safe() const { return UNSAFE_unverified(); }
101 #define BinaryOpValAndPtr(opSymbol) \
102 template<typename T_Rhs> \
103 inline constexpr auto operator opSymbol(const T_Rhs& rhs) \
104 const->tainted<decltype(std::declval<T>() opSymbol std::declval< \
105 detail::rlbox_remove_wrapper_t<T_Rhs>>()), \
106 T_Sbx> \
108 static_assert(detail::is_basic_type_v<T>, \
109 "Operator " #opSymbol \
110 " only supported for primitive and pointer types"); \
112 auto raw_rhs = detail::unwrap_value(rhs); \
114 if constexpr (std::is_pointer_v<T>) { \
115 static_assert(std::is_integral_v<decltype(raw_rhs)>, \
116 "Can only operate on numeric types"); \
117 auto ptr = impl().get_raw_value(); \
118 detail::dynamic_check(ptr != nullptr, \
119 "Pointer arithmetic on a null pointer"); \
120 /* increment the target by size of the data structure */ \
121 auto target = \
122 reinterpret_cast<uintptr_t>(ptr) opSymbol raw_rhs * sizeof(*impl()); \
123 auto no_overflow = rlbox_sandbox<T_Sbx>::is_in_same_sandbox( \
124 reinterpret_cast<const void*>(ptr), \
125 reinterpret_cast<const void*>(target)); \
126 detail::dynamic_check( \
127 no_overflow, \
128 "Pointer arithmetic overflowed a pointer beyond sandbox memory"); \
130 return tainted<T, T_Sbx>::internal_factory(reinterpret_cast<T>(target)); \
131 } else { \
132 auto raw = impl().get_raw_value(); \
133 auto ret = raw opSymbol raw_rhs; \
134 using T_Ret = decltype(ret); \
135 return tainted<T_Ret, T_Sbx>::internal_factory(ret); \
138 RLBOX_REQUIRE_SEMI_COLON
140 BinaryOpValAndPtr(+);
141 BinaryOpValAndPtr(-);
143 #undef BinaryOpValAndPtr
145 #define BinaryOp(opSymbol) \
146 template<typename T_Rhs> \
147 inline constexpr auto operator opSymbol(const T_Rhs& rhs) \
148 const->tainted<decltype(std::declval<T>() opSymbol std::declval< \
149 detail::rlbox_remove_wrapper_t<T_Rhs>>()), \
150 T_Sbx> \
152 static_assert(detail::is_fundamental_or_enum_v<T>, \
153 "Operator " #opSymbol \
154 " only supported for primitive types"); \
156 auto raw = impl().get_raw_value(); \
157 auto raw_rhs = detail::unwrap_value(rhs); \
158 static_assert(std::is_integral_v<decltype(raw_rhs)> || \
159 std::is_floating_point_v<decltype(raw_rhs)>, \
160 "Can only operate on numeric types"); \
162 auto ret = raw opSymbol raw_rhs; \
163 using T_Ret = decltype(ret); \
164 return tainted<T_Ret, T_Sbx>::internal_factory(ret); \
166 RLBOX_REQUIRE_SEMI_COLON
168 BinaryOp(*);
169 BinaryOp(/);
170 BinaryOp(%);
171 BinaryOp(^);
172 BinaryOp(&);
173 BinaryOp(|);
174 BinaryOp(<<);
175 BinaryOp(>>);
177 #undef BinaryOp
179 #define CompoundAssignmentOp(opSymbol) \
180 template<typename T_Rhs> \
181 inline constexpr T_Wrap<T, T_Sbx>& operator opSymbol##=(const T_Rhs& rhs) \
183 auto& this_ref = impl(); \
184 this_ref = this_ref opSymbol rhs; \
185 return this_ref; \
187 RLBOX_REQUIRE_SEMI_COLON
189 CompoundAssignmentOp(+);
190 CompoundAssignmentOp(-);
191 CompoundAssignmentOp(*);
192 CompoundAssignmentOp(/);
193 CompoundAssignmentOp(%);
194 CompoundAssignmentOp(^);
195 CompoundAssignmentOp(&);
196 CompoundAssignmentOp(|);
197 CompoundAssignmentOp(<<);
198 CompoundAssignmentOp(>>);
200 #undef CompoundAssignmentOp
202 #define PreIncDecOps(opSymbol) \
203 inline constexpr T_Wrap<T, T_Sbx>& operator opSymbol##opSymbol() \
205 auto& this_ref = impl(); \
206 this_ref = this_ref opSymbol 1; \
207 return this_ref; \
209 RLBOX_REQUIRE_SEMI_COLON
211 PreIncDecOps(+);
212 PreIncDecOps(-);
214 #undef PreIncDecOps
216 #define PostIncDecOps(opSymbol) \
217 inline constexpr T_Wrap<T, T_Sbx> operator opSymbol##opSymbol(int) \
219 tainted<T, T_Sbx> ret = impl(); \
220 operator++(); \
221 return ret; \
223 RLBOX_REQUIRE_SEMI_COLON
225 PostIncDecOps(+);
226 PostIncDecOps(-);
228 #undef PostIncDecOps
230 #define BooleanBinaryOp(opSymbol) \
231 template<typename T_Rhs> \
232 inline constexpr auto operator opSymbol(const T_Rhs& rhs) \
233 const->tainted<decltype(std::declval<T>() opSymbol std::declval< \
234 detail::rlbox_remove_wrapper_t<T_Rhs>>()), \
235 T_Sbx> \
237 static_assert(detail::is_fundamental_or_enum_v<T>, \
238 "Operator " #opSymbol \
239 " only supported for primitive types"); \
241 auto raw = impl().get_raw_value(); \
242 auto raw_rhs = detail::unwrap_value(rhs); \
243 static_assert(std::is_integral_v<decltype(raw_rhs)>, \
244 "Can only operate on numeric types"); \
246 auto ret = raw opSymbol raw_rhs; \
247 using T_Ret = decltype(ret); \
248 return tainted<T_Ret, T_Sbx>::internal_factory(ret); \
251 template<typename T_Rhs> \
252 inline constexpr auto operator opSymbol(const T_Rhs&&) \
253 const->tainted<decltype(std::declval<T>() opSymbol std::declval< \
254 detail::rlbox_remove_wrapper_t<T_Rhs>>()), \
255 T_Sbx> \
257 rlbox_detail_static_fail_because( \
258 detail::true_v<T_Rhs>, \
259 "C++ does not permit safe overloading of && and || operations as this " \
260 "affects the short circuiting behaviour of these operations. RLBox " \
261 "does let you use && and || with tainted in limited situations - when " \
262 "all arguments starting from the second are local variables. It does " \
263 "not allow it if arguments starting from the second are expressions.\n" \
264 "For example the following is not allowed\n" \
265 "\n" \
266 "tainted<bool, T_Sbx> a = true;\n" \
267 "auto r = a && true && sandbox.invoke_sandbox_function(getBool);\n" \
268 "\n" \
269 "However the following would be allowed\n" \
270 "tainted<bool, T_Sbx> a = true;\n" \
271 "auto b = true\n" \
272 "auto c = sandbox.invoke_sandbox_function(getBool);\n" \
273 "auto r = a && b && c;\n" \
274 "\n" \
275 "Note that these 2 programs are not identical. The first program may " \
276 "or may not call getBool, while second program always calls getBool"); \
277 return tainted<bool, T_Sbx>(false); \
279 RLBOX_REQUIRE_SEMI_COLON
281 BooleanBinaryOp(&&);
282 BooleanBinaryOp(||);
284 #undef BooleanBinaryOp
286 #define UnaryOp(opSymbol) \
287 inline auto operator opSymbol() \
289 static_assert(detail::is_fundamental_or_enum_v<T>, \
290 "Operator " #opSymbol " only supported for primitive"); \
292 auto raw = impl().get_raw_value(); \
293 auto ret = opSymbol raw; \
294 using T_Ret = decltype(ret); \
295 return tainted<T_Ret, T_Sbx>::internal_factory(ret); \
297 RLBOX_REQUIRE_SEMI_COLON
299 UnaryOp(-);
300 UnaryOp(~);
302 #undef UnaryOp
305 * @brief Comparison operators. Comparisons to values in sandbox memory can
306 * only return a "tainted_boolean_hint" as the values in memory can be
307 * incorrect or malicously change in the future.
309 * @tparam T_Rhs
310 * @param rhs
311 * @return One of either a bool, tainted<bool>, or a tainted_boolean_hint
312 * depending on the arguments to the binary expression.
314 #define CompareOp(opSymbol, permit_pointers) \
315 template<typename T_Rhs> \
316 inline constexpr auto operator opSymbol(const T_Rhs& rhs) const \
318 using T_RhsNoQ = detail::remove_cv_ref_t<T_Rhs>; \
319 constexpr bool check_rhs_hint = \
320 detail::rlbox_is_tainted_volatile_v<T_RhsNoQ> || \
321 detail::rlbox_is_tainted_boolean_hint_v<T_RhsNoQ>; \
322 constexpr bool check_lhs_hint = \
323 detail::rlbox_is_tainted_volatile_v<T_Wrap<T, T_Sbx>>; \
324 constexpr bool is_hint = check_lhs_hint || check_rhs_hint; \
326 constexpr bool is_unwrapped = \
327 detail::rlbox_is_tainted_v<T_Wrap<T, T_Sbx>> && \
328 std::is_null_pointer_v<T_RhsNoQ>; \
330 /* Sanity check - can't be a hint and unwrapped */ \
331 static_assert(is_hint ? !is_unwrapped : true, \
332 "Internal error: Could not deduce type for comparison. " \
333 "Please file a bug."); \
335 if constexpr (!permit_pointers && std::is_pointer_v<T>) { \
336 rlbox_detail_static_fail_because( \
337 std::is_pointer_v<T>, \
338 "Only == and != comparisons are allowed for pointers"); \
341 bool ret = (impl().get_raw_value() opSymbol detail::unwrap_value(rhs)); \
343 if constexpr (is_hint) { \
344 return tainted_boolean_hint(ret); \
345 } else if constexpr (is_unwrapped) { \
346 return ret; \
347 } else { \
348 return tainted<bool, T_Sbx>(ret); \
351 RLBOX_REQUIRE_SEMI_COLON
353 CompareOp(==, true /* permit_pointers */);
354 CompareOp(!=, true /* permit_pointers */);
355 CompareOp(<, false /* permit_pointers */);
356 CompareOp(<=, false /* permit_pointers */);
357 CompareOp(>, false /* permit_pointers */);
358 CompareOp(>=, false /* permit_pointers */);
360 #undef CompareOp
362 private:
363 using T_OpSubscriptArrRet = std::conditional_t<
364 std::is_pointer_v<T>,
365 tainted_volatile<detail::dereference_result_t<T>, T_Sbx>, // is_pointer
366 T_Wrap<detail::dereference_result_t<T>, T_Sbx> // is_array
369 public:
370 template<typename T_Rhs>
371 inline const T_OpSubscriptArrRet& operator[](T_Rhs&& rhs) const
373 static_assert(std::is_pointer_v<T> || detail::is_c_or_std_array_v<T>,
374 "Operator [] supports pointers and arrays only");
376 auto raw_rhs = detail::unwrap_value(rhs);
377 static_assert(std::is_integral_v<decltype(raw_rhs)>,
378 "Can only index with numeric types");
380 if constexpr (std::is_pointer_v<T>) {
381 auto ptr = this->impl().get_raw_value();
383 // increment the target by size of the data structure
384 auto target =
385 reinterpret_cast<uintptr_t>(ptr) + raw_rhs * sizeof(*this->impl());
386 auto no_overflow = rlbox_sandbox<T_Sbx>::is_in_same_sandbox(
387 ptr, reinterpret_cast<const void*>(target));
388 detail::dynamic_check(
389 no_overflow,
390 "Pointer arithmetic overflowed a pointer beyond sandbox memory");
392 auto target_wrap = tainted<const T, T_Sbx>::internal_factory(
393 reinterpret_cast<const T>(target));
394 return *target_wrap;
395 } else {
396 using T_Rhs_Unsigned = std::make_unsigned_t<decltype(raw_rhs)>;
397 detail::dynamic_check(
398 raw_rhs >= 0 && static_cast<T_Rhs_Unsigned>(raw_rhs) <
399 std::extent_v<detail::std_array_to_c_arr_t<T>, 0>,
400 "Static array indexing overflow");
402 const void* target_ptr;
403 if constexpr (detail::rlbox_is_tainted_v<T_Wrap<T, T_Sbx>>) {
404 auto& data_ref = impl().get_raw_value_ref();
405 target_ptr = &(data_ref[raw_rhs]);
406 } else {
407 auto& data_ref = impl().get_sandbox_value_ref();
408 auto target_ptr_vol = &(data_ref[raw_rhs]);
409 // target_ptr is a volatile... remove this.
410 // Safe as we will return a tainted_volatile if this is the case
411 target_ptr = detail::remove_volatile_from_ptr_cast(target_ptr_vol);
414 using T_Target = const T_Wrap<detail::dereference_result_t<T>, T_Sbx>;
415 auto wrapped_target_ptr = reinterpret_cast<T_Target*>(target_ptr);
416 return *wrapped_target_ptr;
420 template<typename T_Rhs>
421 inline T_OpSubscriptArrRet& operator[](T_Rhs&& rhs)
423 return const_cast<T_OpSubscriptArrRet&>(std::as_const(*this)[rhs]);
426 private:
427 using T_OpDerefRet = tainted_volatile<std::remove_pointer_t<T>, T_Sbx>;
429 public:
430 inline T_OpDerefRet& operator*() const
432 static_assert(std::is_pointer_v<T>, "Operator * only allowed on pointers");
433 auto ret_ptr_const =
434 reinterpret_cast<const T_OpDerefRet*>(impl().get_raw_value());
435 // Safe - If T_OpDerefRet is not a const ptr, this is trivially safe
436 // If T_OpDerefRet is a const ptr, then the const is captured
437 // inside the wrapper
438 auto ret_ptr = const_cast<T_OpDerefRet*>(ret_ptr_const);
439 return *ret_ptr;
442 // We need to implement the -> operator even if T is not a struct
443 // So that we can support code patterns such as the below
444 // tainted<T*> a;
445 // a->UNSAFE_unverified();
446 inline const T_OpDerefRet* operator->() const
448 static_assert(std::is_pointer_v<T>,
449 "Operator -> only supported for pointer types");
450 return reinterpret_cast<const T_OpDerefRet*>(impl().get_raw_value());
453 inline T_OpDerefRet* operator->()
455 return const_cast<T_OpDerefRet*>(std::as_const(*this).operator->());
458 inline auto operator!()
460 if_constexpr_named(cond1, std::is_pointer_v<T>)
462 return impl() == nullptr;
464 else if_constexpr_named(cond2, std::is_same_v<std::remove_cv_t<T>, bool>)
466 return impl() == false;
468 else
470 auto unknownCase = !(cond1 || cond2);
471 rlbox_detail_static_fail_because(
472 unknownCase,
473 "Operator ! only permitted for pointer or boolean types. For other"
474 "types, unwrap the tainted value with the copy_and_verify API and then"
475 "use operator !");
480 * @brief Copy tainted value from sandbox and verify it.
482 * @param verifier Function used to verify the copied value.
483 * @tparam T_Func the type of the verifier.
484 * @return Whatever the verifier function returns.
486 template<typename T_Func>
487 inline auto copy_and_verify(T_Func verifier) const
489 using T_Deref = std::remove_cv_t<std::remove_pointer_t<T>>;
491 if_constexpr_named(cond1, detail::is_fundamental_or_enum_v<T>)
493 auto val = impl().get_raw_value();
494 return verifier(val);
496 else if_constexpr_named(
497 cond2, detail::is_one_level_ptr_v<T> && !std::is_class_v<T_Deref>)
499 // Some paths don't use the verifier
500 RLBOX_UNUSED(verifier);
502 if_constexpr_named(subcond1, std::is_void_v<T_Deref>)
504 rlbox_detail_static_fail_because(
505 subcond1,
506 "copy_and_verify not recommended for void* as it could lead to some "
507 "anti-patterns in verifiers. Cast it to a different tainted pointer "
508 "with sandbox_reinterpret_cast and then call copy_and_verify. "
509 "Alternately, you can use the UNSAFE_unverified API to do this "
510 "without casting.");
511 return nullptr;
513 // Test with detail::is_func_ptr_v to check for member funcs also
514 else if_constexpr_named(subcond2, detail::is_func_ptr_v<T>)
516 rlbox_detail_static_fail_because(
517 subcond2,
518 "copy_and_verify cannot be applied to function pointers as this "
519 "makes a deep copy. This is not possible for function pointers. "
520 "Consider copy_and_verify_address instead.");
521 return nullptr;
523 else
525 auto val = impl().get_raw_value();
526 if (val == nullptr) {
527 return verifier(nullptr);
528 } else {
529 // Important to assign to a local variable (i.e. make a copy)
530 // Else, for tainted_volatile, this will allow a
531 // time-of-check-time-of-use attack
532 auto val_copy = std::make_unique<T_Deref>();
533 *val_copy = *val;
534 return verifier(std::move(val_copy));
538 else if_constexpr_named(
539 cond3, detail::is_one_level_ptr_v<T> && std::is_class_v<T_Deref>)
541 auto val_copy = std::make_unique<tainted<T_Deref, T_Sbx>>(*impl());
542 return verifier(std::move(val_copy));
544 else if_constexpr_named(cond4, std::is_array_v<T>)
546 static_assert(
547 detail::is_fundamental_or_enum_v<std::remove_all_extents_t<T>>,
548 "copy_and_verify on arrays is only safe for fundamental or enum types. "
549 "For arrays of other types, apply copy_and_verify on each element "
550 "individually --- a[i].copy_and_verify(...)");
552 auto copy = impl().get_raw_value();
553 return verifier(copy);
555 else
557 auto unknownCase = !(cond1 || cond2 || cond3 || cond4);
558 rlbox_detail_static_fail_because(
559 unknownCase,
560 "copy_and_verify not supported for this type as it may be unsafe");
564 private:
565 using T_CopyAndVerifyRangeEl =
566 detail::valid_array_el_t<std::remove_cv_t<std::remove_pointer_t<T>>>;
568 // Template needed to ensure that function isn't instantiated for unsupported
569 // types like function pointers which causes compile errors...
570 template<typename T2 = T>
571 inline const void* verify_range_helper(std::size_t count) const
573 static_assert(std::is_pointer_v<T>);
574 static_assert(detail::is_fundamental_or_enum_v<T_CopyAndVerifyRangeEl>);
576 detail::dynamic_check(
577 count != 0,
578 "Called copy_and_verify_range/copy_and_verify_string with count 0");
580 auto start = reinterpret_cast<const void*>(impl().get_raw_value());
581 if (start == nullptr) {
582 return nullptr;
585 detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(
586 start, count * sizeof(T_CopyAndVerifyRangeEl));
588 return start;
591 template<typename T2 = T>
592 inline std::unique_ptr<T_CopyAndVerifyRangeEl[]> copy_and_verify_range_helper(
593 std::size_t count) const
595 const void* start = verify_range_helper(count);
596 if (start == nullptr) {
597 return nullptr;
600 auto target = std::make_unique<T_CopyAndVerifyRangeEl[]>(count);
602 for (size_t i = 0; i < count; i++) {
603 auto p_src_i_tainted = &(impl()[i]);
604 auto p_src_i = p_src_i_tainted.get_raw_value();
605 detail::convert_type_fundamental_or_array(target[i], *p_src_i);
608 return target;
611 public:
613 * @brief Copy a range of tainted values from sandbox and verify them.
615 * @param verifier Function used to verify the copied value.
616 * @param count Number of elements to copy.
617 * @tparam T_Func the type of the verifier. If the tainted type is ``int*``
618 * then ``T_Func = T_Ret(*)(unique_ptr<int[]>)``.
619 * @return Whatever the verifier function returns.
621 template<typename T_Func>
622 inline auto copy_and_verify_range(T_Func verifier, std::size_t count) const
624 static_assert(std::is_pointer_v<T>,
625 "Can only call copy_and_verify_range on pointers");
627 static_assert(
628 detail::is_fundamental_or_enum_v<T_CopyAndVerifyRangeEl>,
629 "copy_and_verify_range is only safe for ranges of "
630 "fundamental or enum types. For other types, call "
631 "copy_and_verify on each element --- a[i].copy_and_verify(...)");
633 std::unique_ptr<T_CopyAndVerifyRangeEl[]> target =
634 copy_and_verify_range_helper(count);
635 return verifier(std::move(target));
639 * @brief Copy a tainted string from sandbox and verify it.
641 * @param verifier Function used to verify the copied value.
642 * @tparam T_Func the type of the verifier either
643 * ``T_Ret(*)(unique_ptr<char[]>)`` or ``T_Ret(*)(std::string)``
644 * @return Whatever the verifier function returns.
646 template<typename T_Func>
647 inline auto copy_and_verify_string(T_Func verifier) const
649 static_assert(std::is_pointer_v<T>,
650 "Can only call copy_and_verify_string on pointers");
652 static_assert(std::is_same_v<char, T_CopyAndVerifyRangeEl>,
653 "copy_and_verify_string only allows char*");
655 using T_VerifParam = detail::func_first_arg_t<T_Func>;
657 auto start = impl().get_raw_value();
658 if_constexpr_named(
659 cond1,
660 std::is_same_v<T_VerifParam, std::unique_ptr<char[]>> ||
661 std::is_same_v<T_VerifParam, std::unique_ptr<const char[]>>)
663 if (start == nullptr) {
664 return verifier(nullptr);
667 // it is safe to run strlen on a tainted<string> as worst case, the string
668 // does not have a null and we try to copy all the memory out of the
669 // sandbox however, copy_and_verify_range ensures that we never copy
670 // memory outsider the range
671 auto str_len = std::strlen(start) + 1;
672 std::unique_ptr<T_CopyAndVerifyRangeEl[]> target =
673 copy_and_verify_range_helper(str_len);
675 // ensure the string has a trailing null
676 target[str_len - 1] = '\0';
678 return verifier(std::move(target));
680 else if_constexpr_named(cond2, std::is_same_v<T_VerifParam, std::string>)
682 if (start == nullptr) {
683 std::string param = "";
684 return verifier(param);
687 // it is safe to run strlen on a tainted<string> as worst case, the string
688 // does not have a null and we try to copy all the memory out of the
689 // sandbox however, copy_and_verify_range ensures that we never copy
690 // memory outsider the range
691 auto str_len = std::strlen(start) + 1;
693 const char* checked_start = (const char*)verify_range_helper(str_len);
694 if (checked_start == nullptr) {
695 std::string param = "";
696 return verifier(param);
699 std::string copy(checked_start, str_len - 1);
700 return verifier(std::move(copy));
702 else
704 constexpr bool unknownCase = !(cond1 || cond2);
705 rlbox_detail_static_fail_because(
706 unknownCase,
707 "copy_and_verify_string verifier parameter should either be "
708 "unique_ptr<char[]>, unique_ptr<const char[]> or std::string");
713 * @brief Copy a tainted pointer from sandbox and verify the address.
715 * This function is useful if you need to verify physical bits representing
716 * the address of a pointer. Other APIs such as copy_and_verify performs a
717 * deep copy and changes the address bits.
719 * @param verifier Function used to verify the copied value.
720 * @tparam T_Func the type of the verifier ``T_Ret(*)(uintptr_t)``
721 * @return Whatever the verifier function returns.
723 template<typename T_Func>
724 inline auto copy_and_verify_address(T_Func verifier) const
726 static_assert(std::is_pointer_v<T>,
727 "copy_and_verify_address must be used on pointers");
728 auto val = reinterpret_cast<uintptr_t>(impl().get_raw_value());
729 return verifier(val);
733 * @brief Copy a tainted pointer to a buffer from sandbox and verify the
734 * address.
736 * This function is useful if you need to verify physical bits representing
737 * the address of a buffer. Other APIs such as copy_and_verify performs a
738 * deep copy and changes the address bits.
740 * @param verifier Function used to verify the copied value.
741 * @param size Size of the buffer. Buffer with length size is expected to fit
742 * inside sandbox memory.
743 * @tparam T_Func the type of the verifier ``T_Ret(*)(uintptr_t)``
744 * @return Whatever the verifier function returns.
746 template<typename T_Func>
747 inline auto copy_and_verify_buffer_address(T_Func verifier,
748 std::size_t size) const
750 static_assert(std::is_pointer_v<T>,
751 "copy_and_verify_address must be used on pointers");
752 auto val = reinterpret_cast<uintptr_t>(verify_range_helper(size));
753 return verifier(val);
757 #define BinaryOpWrappedRhs(opSymbol) \
758 template<template<typename, typename> typename T_Wrap, \
759 typename T, \
760 typename T_Sbx, \
761 typename T_Lhs, \
762 RLBOX_ENABLE_IF(!detail::rlbox_is_wrapper_v<T_Lhs> && \
763 !detail::rlbox_is_tainted_boolean_hint_v<T_Lhs>)> \
764 inline constexpr auto operator opSymbol( \
765 const T_Lhs& lhs, const tainted_base_impl<T_Wrap, T, T_Sbx>& rhs) \
767 /* Handles the case for "3 + tainted", where + is a binary op */ \
768 /* Technically pointer arithmetic can be performed as 3 + tainted_ptr */ \
769 /* as well. However, this is unusual and to keep the code simple we do */ \
770 /* not support this. */ \
771 static_assert( \
772 std::is_arithmetic_v<T_Lhs>, \
773 "Binary expressions between an non tainted type and tainted" \
774 "type is only permitted if the first value is the tainted type. Try " \
775 "changing the order of the binary expression accordingly"); \
776 auto ret = tainted<T_Lhs, T_Sbx>(lhs) opSymbol rhs.impl(); \
777 return ret; \
779 RLBOX_REQUIRE_SEMI_COLON
781 BinaryOpWrappedRhs(+);
782 BinaryOpWrappedRhs(-);
783 BinaryOpWrappedRhs(*);
784 BinaryOpWrappedRhs(/);
785 BinaryOpWrappedRhs(%);
786 BinaryOpWrappedRhs(^);
787 BinaryOpWrappedRhs(&);
788 BinaryOpWrappedRhs(|);
789 BinaryOpWrappedRhs(<<);
790 BinaryOpWrappedRhs(>>);
791 BinaryOpWrappedRhs(==);
792 BinaryOpWrappedRhs(!=);
793 BinaryOpWrappedRhs(<);
794 BinaryOpWrappedRhs(<=);
795 BinaryOpWrappedRhs(>);
796 BinaryOpWrappedRhs(>=);
797 #undef BinaryOpWrappedRhs
799 #define BooleanBinaryOpWrappedRhs(opSymbol) \
800 template<template<typename, typename> typename T_Wrap, \
801 typename T, \
802 typename T_Sbx, \
803 typename T_Lhs, \
804 RLBOX_ENABLE_IF(!detail::rlbox_is_wrapper_v<T_Lhs> && \
805 !detail::rlbox_is_tainted_boolean_hint_v<T_Lhs>)> \
806 inline constexpr auto operator opSymbol( \
807 const T_Lhs& lhs, const tainted_base_impl<T_Wrap, T, T_Sbx>& rhs) \
809 static_assert( \
810 std::is_arithmetic_v<T_Lhs>, \
811 "Binary expressions between an non tainted type and tainted" \
812 "type is only permitted if the first value is the tainted type. Try " \
813 "changing the order of the binary expression accordingly"); \
814 auto ret = tainted<T_Lhs, T_Sbx>(lhs) opSymbol rhs.impl(); \
815 return ret; \
818 template<template<typename, typename> typename T_Wrap, \
819 typename T, \
820 typename T_Sbx, \
821 typename T_Lhs, \
822 RLBOX_ENABLE_IF(!detail::rlbox_is_wrapper_v<T_Lhs> && \
823 !detail::rlbox_is_tainted_boolean_hint_v<T_Lhs>)> \
824 inline constexpr auto operator opSymbol( \
825 const T_Lhs&, const tainted_base_impl<T_Wrap, T, T_Sbx>&&) \
827 rlbox_detail_static_fail_because( \
828 detail::true_v<T_Lhs>, \
829 "C++ does not permit safe overloading of && and || operations as this " \
830 "affects the short circuiting behaviour of these operations. RLBox " \
831 "does let you use && and || with tainted in limited situations - when " \
832 "all arguments starting from the second are local variables. It does " \
833 "not allow it if arguments starting from the second are expressions.\n" \
834 "For example the following is not allowed\n" \
835 "\n" \
836 "tainted<bool, T_Sbx> a = true;\n" \
837 "auto r = a && true && sandbox.invoke_sandbox_function(getBool);\n" \
838 "\n" \
839 "However the following would be allowed\n" \
840 "tainted<bool, T_Sbx> a = true;\n" \
841 "auto b = true\n" \
842 "auto c = sandbox.invoke_sandbox_function(getBool);\n" \
843 "auto r = a && b && c;\n" \
844 "\n" \
845 "Note that these 2 programs are not identical. The first program may " \
846 "or may not call getBool, while second program always calls getBool"); \
847 return tainted<bool, T_Sbx>(false); \
849 RLBOX_REQUIRE_SEMI_COLON
851 BooleanBinaryOpWrappedRhs(&&);
852 BooleanBinaryOpWrappedRhs(||);
853 #undef BooleanBinaryOpWrappedRhs
855 namespace tainted_detail {
856 template<typename T, typename T_Sbx>
857 using tainted_repr_t = detail::c_to_std_array_t<T>;
859 template<typename T, typename T_Sbx>
860 using tainted_vol_repr_t =
861 detail::c_to_std_array_t<std::add_volatile_t<typename rlbox_sandbox<
862 T_Sbx>::template convert_to_sandbox_equivalent_nonclass_t<T>>>;
866 * @brief Tainted values represent untrusted values that originate from the
867 * sandbox.
869 template<typename T, typename T_Sbx>
870 class tainted : public tainted_base_impl<tainted, T, T_Sbx>
872 KEEP_CLASSES_FRIENDLY
873 KEEP_CAST_FRIENDLY
875 // Classes recieve their own specialization
876 static_assert(
877 !std::is_class_v<T>,
878 "Missing definition for class T. This error occurs for one "
879 "of 2 reasons.\n"
880 " 1) Make sure you have include a call rlbox_load_structs_from_library "
881 "for this library with this class included.\n"
882 " 2) Make sure you run (re-run) the struct-dump tool to list "
883 "all structs in use by your program.\n");
885 static_assert(
886 detail::is_basic_type_v<T> || std::is_array_v<T>,
887 "Tainted types only support fundamental, enum, pointer, array and struct "
888 "types. Please file a bug if more support is needed.");
890 private:
891 using T_ClassBase = tainted_base_impl<tainted, T, T_Sbx>;
892 using T_AppType = tainted_detail::tainted_repr_t<T, T_Sbx>;
893 using T_SandboxedType = tainted_detail::tainted_vol_repr_t<T, T_Sbx>;
894 T_AppType data;
896 inline auto& get_raw_value_ref() noexcept { return data; }
897 inline auto& get_raw_value_ref() const noexcept { return data; }
899 inline std::remove_cv_t<T_AppType> get_raw_value() const noexcept
901 return data;
904 inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value(
905 rlbox_sandbox<T_Sbx>& sandbox) const
907 std::remove_cv_t<T_SandboxedType> ret;
909 using namespace detail;
910 convert_type_non_class<T_Sbx,
911 adjust_type_direction::TO_SANDBOX,
912 adjust_type_context::SANDBOX>(
913 ret, data, nullptr /* example_unsandboxed_ptr */, &sandbox);
914 return ret;
917 inline const void* find_example_pointer_or_null() const noexcept
919 if constexpr (std::is_array_v<T>) {
920 auto& data_ref = get_raw_value_ref();
922 for (size_t i = 0; i < std::extent_v<T>; i++) {
923 const void* ret = data[i].find_example_pointer_or_null();
924 if (ret != nullptr) {
925 return ret;
928 } else if constexpr (std::is_pointer_v<T> && !detail::is_func_ptr_v<T>) {
929 auto data = get_raw_value();
930 return data;
932 return nullptr;
935 // Initializing with a pointer is dangerous and permitted only internally
936 template<typename T2 = T, RLBOX_ENABLE_IF(std::is_pointer_v<T2>)>
937 tainted(T2 val, const void* /* internal_tag */)
938 : data(val)
940 // Sanity check
941 static_assert(std::is_pointer_v<T>);
944 template<typename T_Rhs>
945 static inline tainted<T, T_Sbx> internal_factory(T_Rhs&& rhs)
947 if constexpr (std::is_pointer_v<std::remove_reference_t<T_Rhs>>) {
948 const void* internal_tag = nullptr;
949 return tainted(std::forward<T_Rhs>(rhs), internal_tag);
950 } else {
951 return tainted(std::forward<T_Rhs>(rhs));
955 public:
956 tainted() = default;
957 tainted(const tainted<T, T_Sbx>& p) = default;
959 tainted(const tainted_volatile<T, T_Sbx>& p)
961 // Need to construct an example_unsandboxed_ptr for pointers or arrays of
962 // pointers. Since tainted_volatile is the type of data in sandbox memory,
963 // the address of data (&data) refers to a location in sandbox memory and
964 // can thus be the example_unsandboxed_ptr
965 const volatile void* p_data_ref = &p.get_sandbox_value_ref();
966 const void* example_unsandboxed_ptr = const_cast<const void*>(p_data_ref);
967 using namespace detail;
968 convert_type_non_class<T_Sbx,
969 adjust_type_direction::TO_APPLICATION,
970 adjust_type_context::EXAMPLE>(
971 get_raw_value_ref(),
972 p.get_sandbox_value_ref(),
973 example_unsandboxed_ptr,
974 nullptr /* sandbox_ptr */);
977 // Initializing with a pointer is dangerous and permitted only internally
978 template<typename T2 = T, RLBOX_ENABLE_IF(std::is_pointer_v<T2>)>
979 tainted(T2 val)
980 : data(val)
982 rlbox_detail_static_fail_because(
983 std::is_pointer_v<T2>,
984 "Assignment of pointers is not safe as it could\n "
985 "1) Leak pointers from the appliction to the sandbox which may break "
986 "ASLR\n "
987 "2) Pass inaccessible pointers to the sandbox leading to crash\n "
988 "3) Break sandboxes that require pointers to be swizzled first\n "
989 "\n "
990 "Instead, if you want to pass in a pointer, do one of the following\n "
991 "1) Allocate with malloc_in_sandbox, and pass in a tainted pointer\n "
992 "2) For pointers that point to functions in the application, register "
993 "with sandbox.register_callback(\"foo\"), and pass in the registered "
994 "value\n "
995 "3) For pointers that point to functions in the sandbox, get the "
996 "address with get_sandbox_function_address(sandbox, foo), and pass in "
997 "the "
998 "address\n "
999 "4) For raw pointers, use assign_raw_pointer which performs required "
1000 "safety checks\n ");
1003 tainted(
1004 const sandbox_callback<
1005 detail::function_ptr_t<T> // Need to ensure we never generate code that
1006 // creates a sandbox_callback of a non function
1008 T_Sbx>&)
1010 rlbox_detail_static_fail_because(
1011 detail::true_v<T>,
1012 "RLBox does not support assigning sandbox_callback values to tainted "
1013 "types (i.e. types that live in application memory).\n"
1014 "If you still want to do this, consider changing your code to store the "
1015 "value in sandbox memory as follows. Convert\n\n"
1016 "sandbox_callback<T_Func, Sbx> cb = ...;\n"
1017 "tainted<T_Func, Sbx> foo = cb;\n\n"
1018 "to\n\n"
1019 "tainted<T_Func*, Sbx> foo_ptr = sandbox.malloc_in_sandbox<T_Func*>();\n"
1020 "*foo_ptr = cb;\n\n"
1021 "This would keep the assignment in sandbox memory");
1024 tainted(const std::nullptr_t& arg)
1025 : data(arg)
1027 static_assert(std::is_pointer_v<T>);
1030 // We explicitly disable this constructor if it has one of the signatures
1031 // above, so that we give the above constructors a higher priority. We only
1032 // allow this for fundamental types as this is potentially unsafe for pointers
1033 // and structs
1034 template<typename T_Arg,
1035 RLBOX_ENABLE_IF(
1036 !detail::rlbox_is_wrapper_v<std::remove_reference_t<T_Arg>> &&
1037 detail::is_fundamental_or_enum_v<T> &&
1038 detail::is_fundamental_or_enum_v<std::remove_reference_t<T_Arg>>)>
1039 tainted(T_Arg&& arg)
1040 : data(std::forward<T_Arg>(arg))
1043 template<typename T_Rhs>
1044 void assign_raw_pointer(rlbox_sandbox<T_Sbx>& sandbox, T_Rhs val)
1046 static_assert(std::is_pointer_v<T_Rhs>, "Must be a pointer");
1047 static_assert(std::is_assignable_v<T&, T_Rhs>,
1048 "Should assign pointers of compatible types.");
1049 // Maybe a function pointer, so we need to cast
1050 const void* cast_val = reinterpret_cast<const void*>(val);
1051 bool safe = sandbox.is_pointer_in_sandbox_memory(cast_val);
1052 detail::dynamic_check(
1053 safe,
1054 "Tried to assign a pointer that is not in the sandbox.\n "
1055 "This is not safe as it could\n "
1056 "1) Leak pointers from the appliction to the sandbox which may break "
1057 "ASLR\n "
1058 "2) Pass inaccessible pointers to the sandbox leading to crash\n "
1059 "3) Break sandboxes that require pointers to be swizzled first\n "
1060 "\n "
1061 "Instead, if you want to pass in a pointer, do one of the following\n "
1062 "1) Allocate with malloc_in_sandbox, and pass in a tainted pointer\n "
1063 "2) For pointers that point to functions in the application, register "
1064 "with sandbox.register_callback(\"foo\"), and pass in the registered "
1065 "value\n "
1066 "3) For pointers that point to functions in the sandbox, get the "
1067 "address with get_sandbox_function_address(sandbox, foo), and pass in "
1068 "the "
1069 "address\n ");
1070 data = val;
1073 inline tainted_opaque<T, T_Sbx> to_opaque()
1075 return *reinterpret_cast<tainted_opaque<T, T_Sbx>*>(this);
1078 template<typename T_Dummy = void>
1079 operator bool() const
1081 if_constexpr_named(cond1, std::is_pointer_v<T>)
1083 // We return this without the tainted wrapper as the checking for null
1084 // doesn't really "induce" tainting in the application If the
1085 // application is checking this pointer for null, then it is robust to
1086 // this pointer being null or not null
1087 return get_raw_value() != nullptr;
1089 else
1091 auto unknownCase = !(cond1);
1092 rlbox_detail_static_fail_because(
1093 unknownCase,
1094 "Implicit conversion to bool is only permitted for pointer types. For "
1095 "other types, unwrap the tainted value with the copy_and_verify API "
1096 "and then perform the required checks");
1101 template<typename T, typename T_Sbx>
1102 inline tainted<T, T_Sbx> from_opaque(tainted_opaque<T, T_Sbx> val)
1104 return *reinterpret_cast<tainted<T, T_Sbx>*>(&val);
1108 * @brief Tainted volatile values are like tainted values but still point to
1109 * sandbox memory. Dereferencing a tainted pointer produces a tainted_volatile.
1111 template<typename T, typename T_Sbx>
1112 class tainted_volatile : public tainted_base_impl<tainted_volatile, T, T_Sbx>
1114 KEEP_CLASSES_FRIENDLY
1115 KEEP_CAST_FRIENDLY
1117 // Classes recieve their own specialization
1118 static_assert(
1119 !std::is_class_v<T>,
1120 "Missing definition for class T. This error occurs for one "
1121 "of 2 reasons.\n"
1122 " 1) Make sure you have include a call rlbox_load_structs_from_library "
1123 "for this library with this class included.\n"
1124 " 2) Make sure you run (re-run) the struct-dump tool to list "
1125 "all structs in use by your program.\n");
1127 static_assert(
1128 detail::is_basic_type_v<T> || std::is_array_v<T>,
1129 "Tainted types only support fundamental, enum, pointer, array and struct "
1130 "types. Please file a bug if more support is needed.");
1132 private:
1133 using T_ClassBase = tainted_base_impl<tainted_volatile, T, T_Sbx>;
1134 using T_AppType = tainted_detail::tainted_repr_t<T, T_Sbx>;
1135 using T_SandboxedType = tainted_detail::tainted_vol_repr_t<T, T_Sbx>;
1136 T_SandboxedType data;
1138 inline auto& get_sandbox_value_ref() noexcept { return data; }
1139 inline auto& get_sandbox_value_ref() const noexcept { return data; }
1141 inline std::remove_cv_t<T_AppType> get_raw_value() const
1143 std::remove_cv_t<T_AppType> ret;
1144 // Need to construct an example_unsandboxed_ptr for pointers or arrays of
1145 // pointers. Since tainted_volatile is the type of data in sandbox memory,
1146 // the address of data (&data) refers to a location in sandbox memory and
1147 // can thus be the example_unsandboxed_ptr
1148 const volatile void* data_ref = &data;
1149 const void* example_unsandboxed_ptr = const_cast<const void*>(data_ref);
1150 using namespace detail;
1151 convert_type_non_class<T_Sbx,
1152 adjust_type_direction::TO_APPLICATION,
1153 adjust_type_context::EXAMPLE>(
1154 ret, data, example_unsandboxed_ptr, nullptr /* sandbox_ptr */);
1155 return ret;
1158 inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value()
1159 const noexcept
1161 return data;
1164 inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value(
1165 rlbox_sandbox<T_Sbx>& sandbox) const noexcept
1167 RLBOX_UNUSED(sandbox);
1168 return data;
1171 tainted_volatile() = default;
1172 tainted_volatile(const tainted_volatile<T, T_Sbx>& p) = default;
1174 public:
1175 inline tainted<const T*, T_Sbx> operator&() const noexcept
1177 auto ref =
1178 detail::remove_volatile_from_ptr_cast(&this->get_sandbox_value_ref());
1179 auto ref_cast = reinterpret_cast<const T*>(ref);
1180 return tainted<const T*, T_Sbx>::internal_factory(ref_cast);
1183 inline tainted<T*, T_Sbx> operator&() noexcept
1185 return sandbox_const_cast<T*>(&std::as_const(*this));
1188 // Needed as the definition of unary & above shadows the base's binary &
1189 rlbox_detail_forward_binop_to_base(&, T_ClassBase);
1191 template<typename T_RhsRef>
1192 inline tainted_volatile<T, T_Sbx>& operator=(T_RhsRef&& val)
1194 using T_Rhs = std::remove_reference_t<T_RhsRef>;
1195 using T_Rhs_El = std::remove_all_extents_t<T_Rhs>;
1197 // Need to construct an example_unsandboxed_ptr for pointers or arrays of
1198 // pointers. Since tainted_volatile is the type of data in sandbox memory,
1199 // the address of data (&data) refers to a location in sandbox memory and
1200 // can thus be the example_unsandboxed_ptr
1201 const volatile void* data_ref = &get_sandbox_value_ref();
1202 const void* example_unsandboxed_ptr = const_cast<const void*>(data_ref);
1203 // Some branches don't use this
1204 RLBOX_UNUSED(example_unsandboxed_ptr);
1206 if_constexpr_named(
1207 cond1, std::is_same_v<std::remove_const_t<T_Rhs>, std::nullptr_t>)
1209 static_assert(std::is_pointer_v<T>,
1210 "Null pointer can only be assigned to pointers");
1211 // assign using an integer instead of nullptr, as the pointer field may be
1212 // represented as integer
1213 data = 0;
1215 else if_constexpr_named(cond2, detail::rlbox_is_tainted_v<T_Rhs>)
1217 using namespace detail;
1218 convert_type_non_class<T_Sbx,
1219 adjust_type_direction::TO_SANDBOX,
1220 adjust_type_context::EXAMPLE>(
1221 get_sandbox_value_ref(),
1222 val.get_raw_value_ref(),
1223 example_unsandboxed_ptr,
1224 nullptr /* sandbox_ptr */);
1226 else if_constexpr_named(cond3, detail::rlbox_is_tainted_volatile_v<T_Rhs>)
1228 using namespace detail;
1229 convert_type_non_class<T_Sbx,
1230 adjust_type_direction::NO_CHANGE,
1231 adjust_type_context::EXAMPLE>(
1232 get_sandbox_value_ref(),
1233 val.get_sandbox_value_ref(),
1234 example_unsandboxed_ptr,
1235 nullptr /* sandbox_ptr */);
1237 else if_constexpr_named(cond4, detail::rlbox_is_sandbox_callback_v<T_Rhs>)
1239 using T_RhsFunc = detail::rlbox_remove_wrapper_t<T_Rhs>;
1241 // need to perform some typechecking to ensure we are assigning compatible
1242 // function pointer types only
1243 if_constexpr_named(subcond1, !std::is_assignable_v<T&, T_RhsFunc>)
1245 rlbox_detail_static_fail_because(
1246 subcond1,
1247 "Trying to assign function pointer to field of incompatible types");
1249 else
1251 // Need to reinterpret_cast as the representation of the signature of a
1252 // callback uses the machine model of the sandbox, while the field uses
1253 // that of the application. But we have already checked above that this
1254 // is safe.
1255 auto func = val.get_raw_sandbox_value();
1256 using T_Cast = std::remove_volatile_t<T_SandboxedType>;
1257 get_sandbox_value_ref() = (T_Cast)func;
1260 else if_constexpr_named(
1261 cond5,
1262 detail::is_fundamental_or_enum_v<T> ||
1263 (std::is_array_v<T> && !std::is_pointer_v<T_Rhs_El>))
1265 detail::convert_type_fundamental_or_array(get_sandbox_value_ref(), val);
1267 else if_constexpr_named(
1268 cond6, std::is_pointer_v<T_Rhs> || std::is_pointer_v<T_Rhs_El>)
1270 rlbox_detail_static_fail_because(
1271 cond6,
1272 "Assignment of pointers is not safe as it could\n "
1273 "1) Leak pointers from the appliction to the sandbox which may break "
1274 "ASLR\n "
1275 "2) Pass inaccessible pointers to the sandbox leading to crash\n "
1276 "3) Break sandboxes that require pointers to be swizzled first\n "
1277 "\n "
1278 "Instead, if you want to pass in a pointer, do one of the following\n "
1279 "1) Allocate with malloc_in_sandbox, and pass in a tainted pointer\n "
1280 "2) For pointers that point to functions in the application, register "
1281 "with sandbox.register_callback(\"foo\"), and pass in the registered "
1282 "value\n "
1283 "3) For pointers that point to functions in the sandbox, get the "
1284 "address with get_sandbox_function_address(sandbox, foo), and pass in "
1285 "the "
1286 "address\n "
1287 "4) For raw pointers, use assign_raw_pointer which performs required "
1288 "safety checks\n ");
1290 else
1292 auto unknownCase =
1293 !(cond1 || cond2 || cond3 || cond4 || cond5 /* || cond6 */);
1294 rlbox_detail_static_fail_because(
1295 unknownCase, "Assignment of the given type of value is not supported");
1298 return *this;
1301 template<typename T_Rhs>
1302 void assign_raw_pointer(rlbox_sandbox<T_Sbx>& sandbox, T_Rhs val)
1304 static_assert(std::is_pointer_v<T_Rhs>, "Must be a pointer");
1305 static_assert(std::is_assignable_v<T&, T_Rhs>,
1306 "Should assign pointers of compatible types.");
1307 // Maybe a function pointer, so we need to cast
1308 const void* cast_val = reinterpret_cast<const void*>(val);
1309 bool safe = sandbox.is_pointer_in_sandbox_memory(cast_val);
1310 detail::dynamic_check(
1311 safe,
1312 "Tried to assign a pointer that is not in the sandbox.\n "
1313 "This is not safe as it could\n "
1314 "1) Leak pointers from the appliction to the sandbox which may break "
1315 "ASLR\n "
1316 "2) Pass inaccessible pointers to the sandbox leading to crash\n "
1317 "3) Break sandboxes that require pointers to be swizzled first\n "
1318 "\n "
1319 "Instead, if you want to pass in a pointer, do one of the following\n "
1320 "1) Allocate with malloc_in_sandbox, and pass in a tainted pointer\n "
1321 "2) For pointers that point to functions in the application, register "
1322 "with sandbox.register_callback(\"foo\"), and pass in the registered "
1323 "value\n "
1324 "3) For pointers that point to functions in the sandbox, get the "
1325 "address with get_sandbox_function_address(sandbox, foo), and pass in "
1326 "the "
1327 "address\n ");
1328 get_sandbox_value_ref() =
1329 sandbox.template get_sandboxed_pointer<T_Rhs>(cast_val);
1332 template<typename T_Dummy = void>
1333 operator bool() const
1335 rlbox_detail_static_fail_because(
1336 detail::true_v<T_Dummy>,
1337 "Cannot apply implicit conversion to bool on values that are located in "
1338 "sandbox memory. This error occurs if you compare a dereferenced value "
1339 "such as the code shown below\n\n"
1340 "tainted<int**> a = ...;\n"
1341 "assert(*a);\n\n"
1342 "Instead you can write this code as \n"
1343 "tainted<int*> temp = *a;\n"
1344 "assert(temp);\n");
1345 return false;