RLBox
rlbox.hpp
1 #pragma once
2 
3 #include <array>
4 #include <cstring>
5 #include <memory>
6 #include <type_traits>
7 #include <utility>
8 
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"
21 
22 namespace rlbox {
23 
24 template<template<typename, typename> typename T_Wrap,
25  typename T,
26  typename T_Sbx>
28 {
29  KEEP_CLASSES_FRIENDLY
30  KEEP_CAST_FRIENDLY
31 
32 public:
33  inline auto& impl() { return *static_cast<T_Wrap<T, T_Sbx>*>(this); }
34  inline auto& impl() const
35  {
36  return *static_cast<const T_Wrap<T, T_Sbx>*>(this);
37  }
38 
43  inline auto UNSAFE_unverified() { return impl().get_raw_value(); }
44  inline auto UNSAFE_unverified() const { return impl().get_raw_value(); }
54  inline auto UNSAFE_sandboxed(rlbox_sandbox<T_Sbx>& sandbox)
55  {
56  return impl().get_raw_sandbox_value(sandbox);
57  }
58  inline auto UNSAFE_sandboxed(rlbox_sandbox<T_Sbx>& sandbox) const
59  {
60  return impl().get_raw_sandbox_value(sandbox);
61  }
62 
70  template<size_t N>
71  inline auto unverified_safe_because(const char (&reason)[N]),
72  {
73  RLBOX_UNUSED(reason);
74  static_assert(!std::is_pointer_v<T>,
75  "unverified_safe_because does not support pointers. Use "
76  "unverified_safe_pointer_because.");
77  return UNSAFE_unverified();
78  });
79 
81  template<size_t N>
82  inline auto unverified_safe_pointer_because(size_t count,
83  const char (&reason)[N]),
84  {
85  RLBOX_UNUSED(reason);
86 
87  static_assert(std::is_pointer_v<T>, "Expected pointer type");
88  using T_Pointed = std::remove_pointer_t<T>;
89  if_constexpr_named(cond1, std::is_pointer_v<T_Pointed>)
90  {
91  rlbox_detail_static_fail_because(
92  cond1,
93  "There is no way to use unverified_safe_pointer_because for "
94  "'pointers to pointers' safely. Use copy_and_verify instead.");
95  return nullptr;
96  }
97 
98  auto ret = UNSAFE_unverified();
99  if (ret != nullptr) {
100  size_t bytes = sizeof(T) * count;
101  detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(ret, bytes);
102  }
103  return ret;
104  });
105 
106  inline auto INTERNAL_unverified_safe() { return UNSAFE_unverified(); }
107  inline auto INTERNAL_unverified_safe() const { return UNSAFE_unverified(); }
108 
109 #define BinaryOpValAndPtr(opSymbol) \
110  template<typename T_Rhs> \
111  inline constexpr auto operator opSymbol(const T_Rhs& rhs) \
112  const->tainted<decltype(std::declval<T>() opSymbol std::declval< \
113  detail::rlbox_remove_wrapper_t<T_Rhs>>()), \
114  T_Sbx> \
115  { \
116  static_assert(detail::is_basic_type_v<T>, \
117  "Operator " #opSymbol \
118  " only supported for primitive and pointer types"); \
119  \
120  auto raw_rhs = detail::unwrap_value(rhs); \
121  \
122  if constexpr (std::is_pointer_v<T>) { \
123  static_assert(std::is_integral_v<decltype(raw_rhs)>, \
124  "Can only operate on numeric types"); \
125  auto ptr = impl().get_raw_value(); \
126  detail::dynamic_check(ptr != nullptr, \
127  "Pointer arithmetic on a null pointer"); \
128  /* increment the target by size of the data structure */ \
129  auto target = \
130  reinterpret_cast<uintptr_t>(ptr) opSymbol raw_rhs * sizeof(*impl()); \
131  auto no_overflow = rlbox_sandbox<T_Sbx>::is_in_same_sandbox( \
132  reinterpret_cast<const void*>(ptr), \
133  reinterpret_cast<const void*>(target)); \
134  detail::dynamic_check( \
135  no_overflow, \
136  "Pointer arithmetic overflowed a pointer beyond sandbox memory"); \
137  \
138  return tainted<T, T_Sbx>::internal_factory(reinterpret_cast<T>(target)); \
139  } else { \
140  auto raw = impl().get_raw_value(); \
141  auto ret = raw opSymbol raw_rhs; \
142  using T_Ret = decltype(ret); \
143  return tainted<T_Ret, T_Sbx>::internal_factory(ret); \
144  } \
145  } \
146  RLBOX_REQUIRE_SEMI_COLON
147 
148  BinaryOpValAndPtr(+);
149  BinaryOpValAndPtr(-);
150 
151 #undef BinaryOpValAndPtr
152 
153 #define BinaryOp(opSymbol) \
154  template<typename T_Rhs> \
155  inline constexpr auto operator opSymbol(const T_Rhs& rhs) \
156  const->tainted<decltype(std::declval<T>() opSymbol std::declval< \
157  detail::rlbox_remove_wrapper_t<T_Rhs>>()), \
158  T_Sbx> \
159  { \
160  static_assert(detail::is_fundamental_or_enum_v<T>, \
161  "Operator " #opSymbol \
162  " only supported for primitive types"); \
163  \
164  auto raw = impl().get_raw_value(); \
165  auto raw_rhs = detail::unwrap_value(rhs); \
166  static_assert(std::is_integral_v<decltype(raw_rhs)>, \
167  "Can only operate on numeric types"); \
168  \
169  auto ret = raw opSymbol raw_rhs; \
170  using T_Ret = decltype(ret); \
171  return tainted<T_Ret, T_Sbx>::internal_factory(ret); \
172  } \
173  RLBOX_REQUIRE_SEMI_COLON
174 
175  BinaryOp(*);
176  BinaryOp(/);
177  BinaryOp(%);
178  BinaryOp(^);
179  BinaryOp(&);
180  BinaryOp(|);
181  BinaryOp(<<);
182  BinaryOp(>>);
183 
184 #undef BinaryOp
185 
186 #define CompoundAssignmentOp(opSymbol) \
187  template<typename T_Rhs> \
188  inline constexpr T_Wrap<T, T_Sbx>& operator opSymbol##=(const T_Rhs& rhs) \
189  { \
190  auto& this_ref = impl(); \
191  this_ref = this_ref opSymbol rhs; \
192  return this_ref; \
193  } \
194  RLBOX_REQUIRE_SEMI_COLON
195 
196  CompoundAssignmentOp(+);
197  CompoundAssignmentOp(-);
198  CompoundAssignmentOp(*);
199  CompoundAssignmentOp(/);
200  CompoundAssignmentOp(%);
201  CompoundAssignmentOp(^);
202  CompoundAssignmentOp(&);
203  CompoundAssignmentOp(|);
204  CompoundAssignmentOp(<<);
205  CompoundAssignmentOp(>>);
206 
207 #undef CompoundAssignmentOp
208 
209 #define PreIncDecOps(opSymbol) \
210  inline constexpr T_Wrap<T, T_Sbx>& operator opSymbol##opSymbol() \
211  { \
212  auto& this_ref = impl(); \
213  this_ref = this_ref opSymbol 1; \
214  return this_ref; \
215  } \
216  RLBOX_REQUIRE_SEMI_COLON
217 
218  PreIncDecOps(+);
219  PreIncDecOps(-);
220 
221 #undef PreIncDecOps
222 
223 #define PostIncDecOps(opSymbol) \
224  inline constexpr T_Wrap<T, T_Sbx> operator opSymbol##opSymbol(int) \
225  { \
226  tainted<T, T_Sbx> ret = impl(); \
227  operator++(); \
228  return ret; \
229  } \
230  RLBOX_REQUIRE_SEMI_COLON
231 
232  PostIncDecOps(+);
233  PostIncDecOps(-);
234 
235 #undef PostIncDecOps
236 
237 #define BooleanBinaryOp(opSymbol) \
238  template<typename T_Rhs> \
239  inline constexpr auto operator opSymbol(const T_Rhs& rhs) \
240  const->tainted<decltype(std::declval<T>() opSymbol std::declval< \
241  detail::rlbox_remove_wrapper_t<T_Rhs>>()), \
242  T_Sbx> \
243  { \
244  static_assert(detail::is_fundamental_or_enum_v<T>, \
245  "Operator " #opSymbol \
246  " only supported for primitive types"); \
247  \
248  auto raw = impl().get_raw_value(); \
249  auto raw_rhs = detail::unwrap_value(rhs); \
250  static_assert(std::is_integral_v<decltype(raw_rhs)>, \
251  "Can only operate on numeric types"); \
252  \
253  auto ret = raw opSymbol raw_rhs; \
254  using T_Ret = decltype(ret); \
255  return tainted<T_Ret, T_Sbx>::internal_factory(ret); \
256  } \
257  \
258  template<typename T_Rhs> \
259  inline constexpr auto operator opSymbol(const T_Rhs&&) \
260  const->tainted<decltype(std::declval<T>() opSymbol std::declval< \
261  detail::rlbox_remove_wrapper_t<T_Rhs>>()), \
262  T_Sbx> \
263  { \
264  rlbox_detail_static_fail_because( \
265  detail::true_v<T_Rhs>, \
266  "C++ does not permit safe overloading of && and || operations as this " \
267  "affects the short circuiting behaviour of these operations. RLBox " \
268  "does let you use && and || with tainted in limited situations - when " \
269  "all arguments starting from the second are local variables. It does " \
270  "not allow it if arguments starting from the second are expressions.\n" \
271  "For example the following is not allowed\n" \
272  "\n" \
273  "tainted<bool, T_Sbx> a = true;\n" \
274  "auto r = a && true && sandbox.invoke_sandbox_function(getBool);\n" \
275  "\n" \
276  "However the following would be allowed\n" \
277  "tainted<bool, T_Sbx> a = true;\n" \
278  "auto b = true\n" \
279  "auto c = sandbox.invoke_sandbox_function(getBool);\n" \
280  "auto r = a && b && c;\n" \
281  "\n" \
282  "Note that these 2 programs are not identical. The first program may " \
283  "or may not call getBool, while second program always calls getBool"); \
284  return tainted<bool, T_Sbx>(false); \
285  } \
286  RLBOX_REQUIRE_SEMI_COLON
287 
288  BooleanBinaryOp(&&);
289  BooleanBinaryOp(||);
290 
291 #undef BooleanBinaryOp
292 
293 #define UnaryOp(opSymbol) \
294  inline auto operator opSymbol() \
295  { \
296  static_assert(detail::is_fundamental_or_enum_v<T>, \
297  "Operator " #opSymbol " only supported for primitive"); \
298  \
299  auto raw = impl().get_raw_value(); \
300  auto ret = opSymbol raw; \
301  using T_Ret = decltype(ret); \
302  return tainted<T_Ret, T_Sbx>::internal_factory(ret); \
303  } \
304  RLBOX_REQUIRE_SEMI_COLON
305 
306  UnaryOp(-);
307  UnaryOp(~);
308 
309 #undef UnaryOp
310 
321 #define CompareOp(opSymbol, permit_pointers) \
322  template<typename T_Rhs> \
323  inline constexpr auto operator opSymbol(const T_Rhs& rhs) const \
324  { \
325  using T_RhsNoQ = detail::remove_cv_ref_t<T_Rhs>; \
326  constexpr bool check_rhs_hint = \
327  detail::rlbox_is_tainted_volatile_v<T_RhsNoQ> || \
328  detail::rlbox_is_tainted_boolean_hint_v<T_RhsNoQ>; \
329  constexpr bool check_lhs_hint = \
330  detail::rlbox_is_tainted_volatile_v<T_Wrap<T, T_Sbx>>; \
331  constexpr bool is_hint = check_lhs_hint || check_rhs_hint; \
332  \
333  constexpr bool is_unwrapped = \
334  detail::rlbox_is_tainted_v<T_Wrap<T, T_Sbx>> && \
335  std::is_null_pointer_v<T_RhsNoQ>; \
336  \
337  /* Sanity check - can't be a hint and unwrapped */ \
338  static_assert(is_hint ? !is_unwrapped : true, \
339  "Internal error: Could not deduce type for comparison. " \
340  "Please file a bug."); \
341  \
342  if constexpr (!permit_pointers && std::is_pointer_v<T>) { \
343  rlbox_detail_static_fail_because( \
344  std::is_pointer_v<T>, \
345  "Only == and != comparisons are allowed for pointers"); \
346  } \
347  \
348  bool ret = (impl().get_raw_value() opSymbol detail::unwrap_value(rhs)); \
349  \
350  if constexpr (is_hint) { \
351  return tainted_boolean_hint(ret); \
352  } else if constexpr (is_unwrapped) { \
353  return ret; \
354  } else { \
355  return tainted<bool, T_Sbx>(ret); \
356  } \
357  } \
358  RLBOX_REQUIRE_SEMI_COLON
359 
360  CompareOp(==, true /* permit_pointers */);
361  CompareOp(!=, true /* permit_pointers */);
362  CompareOp(<, false /* permit_pointers */);
363  CompareOp(<=, false /* permit_pointers */);
364  CompareOp(>, false /* permit_pointers */);
365  CompareOp(>=, false /* permit_pointers */);
366 
367 #undef CompareOp
368 
369 private:
370  using T_OpSubscriptArrRet = std::conditional_t<
371  std::is_pointer_v<T>,
372  tainted_volatile<detail::dereference_result_t<T>, T_Sbx>, // is_pointer
373  T_Wrap<detail::dereference_result_t<T>, T_Sbx> // is_array
374  >;
375 
376 public:
377  template<typename T_Rhs>
378  inline const T_OpSubscriptArrRet& operator[](T_Rhs&& rhs) const
379  {
380  static_assert(std::is_pointer_v<T> || detail::is_c_or_std_array_v<T>,
381  "Operator [] supports pointers and arrays only");
382 
383  auto raw_rhs = detail::unwrap_value(rhs);
384  static_assert(std::is_integral_v<decltype(raw_rhs)>,
385  "Can only index with numeric types");
386 
387  if constexpr (std::is_pointer_v<T>) {
388  auto ptr = this->impl().get_raw_value();
389 
390  // increment the target by size of the data structure
391  auto target =
392  reinterpret_cast<uintptr_t>(ptr) + raw_rhs * sizeof(*this->impl());
393  auto no_overflow = rlbox_sandbox<T_Sbx>::is_in_same_sandbox(
394  ptr, reinterpret_cast<const void*>(target));
395  detail::dynamic_check(
396  no_overflow,
397  "Pointer arithmetic overflowed a pointer beyond sandbox memory");
398 
399  auto target_wrap = tainted<const T, T_Sbx>::internal_factory(
400  reinterpret_cast<const T>(target));
401  return *target_wrap;
402  } else {
403  using T_Rhs_Unsigned = std::make_unsigned_t<decltype(raw_rhs)>;
404  detail::dynamic_check(
405  raw_rhs >= 0 && static_cast<T_Rhs_Unsigned>(raw_rhs) <
406  std::extent_v<detail::std_array_to_c_arr_t<T>, 0>,
407  "Static array indexing overflow");
408 
409  const void* target_ptr;
410  if constexpr (detail::rlbox_is_tainted_v<T_Wrap<T, T_Sbx>>) {
411  auto& data_ref = impl().get_raw_value_ref();
412  target_ptr = &(data_ref[raw_rhs]);
413  } else {
414  auto& data_ref = impl().get_sandbox_value_ref();
415  auto target_ptr_vol = &(data_ref[raw_rhs]);
416  // target_ptr is a volatile... remove this.
417  // Safe as we will return a tainted_volatile if this is the case
418  target_ptr = detail::remove_volatile_from_ptr_cast(target_ptr_vol);
419  }
420 
421  using T_Target = const T_Wrap<detail::dereference_result_t<T>, T_Sbx>;
422  auto wrapped_target_ptr = reinterpret_cast<T_Target*>(target_ptr);
423  return *wrapped_target_ptr;
424  }
425  }
426 
427  template<typename T_Rhs>
428  inline T_OpSubscriptArrRet& operator[](T_Rhs&& rhs)
429  {
430  rlbox_detail_forward_to_const_a(operator[], T_OpSubscriptArrRet&, rhs);
431  }
432 
433 private:
434  using T_OpDerefRet = tainted_volatile<std::remove_pointer_t<T>, T_Sbx>;
435 
436 public:
437  inline T_OpDerefRet& operator*() const
438  {
439  static_assert(std::is_pointer_v<T>, "Operator * only allowed on pointers");
440  auto ret_ptr_const =
441  reinterpret_cast<const T_OpDerefRet*>(impl().get_raw_value());
442  // Safe - If T_OpDerefRet is not a const ptr, this is trivially safe
443  // If T_OpDerefRet is a const ptr, then the const is captured
444  // inside the wrapper
445  auto ret_ptr = const_cast<T_OpDerefRet*>(ret_ptr_const);
446  return *ret_ptr;
447  }
448 
449  inline T_OpDerefRet& operator*()
450  {
451  rlbox_detail_forward_to_const(operator*, T_OpDerefRet&);
452  }
453 
454  // We need to implement the -> operator even if T is not a struct
455  // So that we can support code patterns such as the below
456  // tainted<T*> a;
457  // a->UNSAFE_unverified();
458  inline auto operator->() const
459  {
460  static_assert(std::is_pointer_v<T>,
461  "Operator -> only supported for pointer types");
462  auto ret = impl().get_raw_value();
463  using T_Ret = std::remove_pointer_t<T>;
464  using T_RetWrap = const tainted_volatile<T_Ret, T_Sbx>;
465  return reinterpret_cast<T_RetWrap*>(ret);
466  }
467 
468  inline auto operator->()
469  {
470  using T_Ret = tainted_volatile<std::remove_pointer_t<T>, T_Sbx>*;
471  rlbox_detail_forward_to_const(operator->, T_Ret);
472  }
473 
474  inline auto operator!()
475  {
476  if_constexpr_named(cond1, std::is_pointer_v<T>)
477  {
478  return impl() == nullptr;
479  }
480  else if_constexpr_named(cond2, std::is_same_v<std::remove_cv_t<T>, bool>)
481  {
482  return impl() == false;
483  }
484  else
485  {
486  auto unknownCase = !(cond1 || cond2);
487  rlbox_detail_static_fail_because(
488  unknownCase,
489  "Operator ! only permitted for pointer or boolean types. For other"
490  "types, unwrap the tainted value with the copy_and_verify API and then"
491  "use operator !");
492  }
493  }
494 
502  template<typename T_Func>
503  inline auto copy_and_verify(T_Func verifier) const
504  {
505  using T_Deref = std::remove_cv_t<std::remove_pointer_t<T>>;
506 
507  if_constexpr_named(cond1, detail::is_fundamental_or_enum_v<T>)
508  {
509  auto val = impl().get_raw_value();
510  return verifier(val);
511  }
512  else if_constexpr_named(
513  cond2, detail::is_one_level_ptr_v<T> && !std::is_class_v<T_Deref>)
514  {
515  // Some paths don't use the verifier
516  RLBOX_UNUSED(verifier);
517 
518  if_constexpr_named(subcond1, std::is_void_v<T_Deref>)
519  {
520  rlbox_detail_static_fail_because(
521  subcond1,
522  "copy_and_verify not recommended for void* as it could lead to some "
523  "anti-patterns in verifiers. Cast it to a different tainted pointer "
524  "with sandbox_reinterpret_cast and then call copy_and_verify. "
525  "Alternately, you can use the UNSAFE_unverified API to do this "
526  "without casting.");
527  return nullptr;
528  }
529  // Test with detail::is_func_ptr_v to check for member funcs also
530  else if_constexpr_named(subcond2, detail::is_func_ptr_v<T>)
531  {
532  rlbox_detail_static_fail_because(
533  subcond2,
534  "copy_and_verify cannot be applied to function pointers as this "
535  "makes a deep copy. This is not possible for function pointers. "
536  "Consider copy_and_verify_address instead.");
537  return nullptr;
538  }
539  else
540  {
541  auto val = impl().get_raw_value();
542  if (val == nullptr) {
543  return verifier(nullptr);
544  } else {
545  // Important to assign to a local variable (i.e. make a copy)
546  // Else, for tainted_volatile, this will allow a
547  // time-of-check-time-of-use attack
548  auto val_copy = std::make_unique<T_Deref>();
549  *val_copy = *val;
550  return verifier(std::move(val_copy));
551  }
552  }
553  }
554  else if_constexpr_named(
555  cond3, detail::is_one_level_ptr_v<T> && std::is_class_v<T_Deref>)
556  {
557  auto val_copy = std::make_unique<tainted<T_Deref, T_Sbx>>(*impl());
558  return verifier(std::move(val_copy));
559  }
560  else if_constexpr_named(cond4, std::is_array_v<T>)
561  {
562  static_assert(
563  detail::is_fundamental_or_enum_v<std::remove_all_extents_t<T>>,
564  "copy_and_verify on arrays is only safe for fundamental or enum types. "
565  "For arrays of other types, apply copy_and_verify on each element "
566  "individually --- a[i].copy_and_verify(...)");
567 
568  auto copy = impl().get_raw_value();
569  return verifier(copy);
570  }
571  else
572  {
573  auto unknownCase = !(cond1 || cond2 || cond3 || cond4);
574  rlbox_detail_static_fail_because(
575  unknownCase,
576  "copy_and_verify not supported for this type as it may be unsafe");
577  }
578  }
579 
580 private:
581  using T_CopyAndVerifyRangeEl =
582  detail::valid_array_el_t<std::remove_cv_t<std::remove_pointer_t<T>>>;
583 
584  // Template needed to ensure that function isn't instantiated for unsupported
585  // types like function pointers which causes compile errors...
586  template<typename T2 = T>
587  inline const void* verify_range_helper(std::size_t count) const
588  {
589  static_assert(std::is_pointer_v<T>);
590  static_assert(detail::is_fundamental_or_enum_v<T_CopyAndVerifyRangeEl>);
591 
592  detail::dynamic_check(
593  count != 0,
594  "Called copy_and_verify_range/copy_and_verify_string with count 0");
595 
596  auto start = reinterpret_cast<const void*>(impl().get_raw_value());
597  if (start == nullptr) {
598  return nullptr;
599  }
600 
601  detail::check_range_doesnt_cross_app_sbx_boundary<T_Sbx>(
602  start, count * sizeof(T_CopyAndVerifyRangeEl));
603 
604  return start;
605  }
606 
607  template<typename T2 = T>
608  inline std::unique_ptr<T_CopyAndVerifyRangeEl[]> copy_and_verify_range_helper(
609  std::size_t count) const
610  {
611  const void* start = verify_range_helper(count);
612  if (start == nullptr) {
613  return nullptr;
614  }
615 
616  auto target = std::make_unique<T_CopyAndVerifyRangeEl[]>(count);
617 
618  for (size_t i = 0; i < count; i++) {
619  auto p_src_i_tainted = &(impl()[i]);
620  auto p_src_i = p_src_i_tainted.get_raw_value();
621  detail::convert_type_fundamental_or_array(target[i], *p_src_i);
622  }
623 
624  return target;
625  }
626 
627 public:
637  template<typename T_Func>
638  inline auto copy_and_verify_range(T_Func verifier, std::size_t count) const
639  {
640  static_assert(std::is_pointer_v<T>,
641  "Can only call copy_and_verify_range on pointers");
642 
643  static_assert(
644  detail::is_fundamental_or_enum_v<T_CopyAndVerifyRangeEl>,
645  "copy_and_verify_range is only safe for ranges of "
646  "fundamental or enum types. For other types, call "
647  "copy_and_verify on each element --- a[i].copy_and_verify(...)");
648 
649  std::unique_ptr<T_CopyAndVerifyRangeEl[]> target =
650  copy_and_verify_range_helper(count);
651  return verifier(std::move(target));
652  }
653 
661  template<typename T_Func>
662  inline auto copy_and_verify_string(T_Func verifier) const
663  {
664  static_assert(std::is_pointer_v<T>,
665  "Can only call copy_and_verify_string on pointers");
666 
667  static_assert(std::is_same_v<char, T_CopyAndVerifyRangeEl>,
668  "copy_and_verify_string only allows char*");
669 
670  auto start = impl().get_raw_value();
671  if (start == nullptr) {
672  return verifier(nullptr);
673  }
674 
675  // it is safe to run strlen on a tainted<string> as worst case, the string
676  // does not have a null and we try to copy all the memory out of the sandbox
677  // however, copy_and_verify_range ensures that we never copy memory outsider
678  // the range
679  auto str_len = std::strlen(start) + 1;
680  std::unique_ptr<T_CopyAndVerifyRangeEl[]> target =
681  copy_and_verify_range_helper(str_len);
682 
683  // ensure the string has a trailing null
684  target[str_len - 1] = '\0';
685 
686  return verifier(std::move(target));
687  }
688 
700  template<typename T_Func>
701  inline auto copy_and_verify_address(T_Func verifier)
702  {
703  static_assert(std::is_pointer_v<T>,
704  "copy_and_verify_address must be used on pointers");
705  auto val = reinterpret_cast<uintptr_t>(impl().get_raw_value());
706  return verifier(val);
707  }
708 
723  template<typename T_Func>
724  inline auto copy_and_verify_buffer_address(T_Func verifier, std::size_t size)
725  {
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>(verify_range_helper(size));
729  return verifier(val);
730  }
731 };
732 
733 #define BinaryOpWrappedRhs(opSymbol) \
734  template<template<typename, typename> typename T_Wrap, \
735  typename T, \
736  typename T_Sbx, \
737  typename T_Lhs, \
738  RLBOX_ENABLE_IF(!detail::rlbox_is_wrapper_v<T_Lhs> && \
739  !detail::rlbox_is_tainted_boolean_hint_v<T_Lhs>)> \
740  inline constexpr auto operator opSymbol( \
741  const T_Lhs& lhs, const tainted_base_impl<T_Wrap, T, T_Sbx>& rhs) \
742  { \
743  /* Handles the case for "3 + tainted", where + is a binary op */ \
744  /* Technically pointer arithmetic can be performed as 3 + tainted_ptr */ \
745  /* as well. However, this is unusual and to keep the code simple we do */ \
746  /* not support this. */ \
747  static_assert( \
748  std::is_arithmetic_v<T_Lhs>, \
749  "Binary expressions between an non tainted type and tainted" \
750  "type is only permitted if the first value is the tainted type. Try " \
751  "changing the order of the binary expression accordingly"); \
752  auto ret = tainted<T_Lhs, T_Sbx>(lhs) opSymbol rhs.impl(); \
753  return ret; \
754  } \
755  RLBOX_REQUIRE_SEMI_COLON
756 
757 BinaryOpWrappedRhs(+);
758 BinaryOpWrappedRhs(-);
759 BinaryOpWrappedRhs(*);
760 BinaryOpWrappedRhs(/);
761 BinaryOpWrappedRhs(%);
762 BinaryOpWrappedRhs(^);
763 BinaryOpWrappedRhs(&);
764 BinaryOpWrappedRhs(|);
765 BinaryOpWrappedRhs(<<);
766 BinaryOpWrappedRhs(>>);
767 BinaryOpWrappedRhs(==);
768 BinaryOpWrappedRhs(!=);
769 BinaryOpWrappedRhs(<);
770 BinaryOpWrappedRhs(<=);
771 BinaryOpWrappedRhs(>);
772 BinaryOpWrappedRhs(>=);
773 #undef BinaryOpWrappedRhs
774 
775 #define BooleanBinaryOpWrappedRhs(opSymbol) \
776  template<template<typename, typename> typename T_Wrap, \
777  typename T, \
778  typename T_Sbx, \
779  typename T_Lhs, \
780  RLBOX_ENABLE_IF(!detail::rlbox_is_wrapper_v<T_Lhs> && \
781  !detail::rlbox_is_tainted_boolean_hint_v<T_Lhs>)> \
782  inline constexpr auto operator opSymbol( \
783  const T_Lhs& lhs, const tainted_base_impl<T_Wrap, T, T_Sbx>& rhs) \
784  { \
785  static_assert( \
786  std::is_arithmetic_v<T_Lhs>, \
787  "Binary expressions between an non tainted type and tainted" \
788  "type is only permitted if the first value is the tainted type. Try " \
789  "changing the order of the binary expression accordingly"); \
790  auto ret = tainted<T_Lhs, T_Sbx>(lhs) opSymbol rhs.impl(); \
791  return ret; \
792  } \
793  \
794  template<template<typename, typename> typename T_Wrap, \
795  typename T, \
796  typename T_Sbx, \
797  typename T_Lhs, \
798  RLBOX_ENABLE_IF(!detail::rlbox_is_wrapper_v<T_Lhs> && \
799  !detail::rlbox_is_tainted_boolean_hint_v<T_Lhs>)> \
800  inline constexpr auto operator opSymbol( \
801  const T_Lhs&, const tainted_base_impl<T_Wrap, T, T_Sbx>&&) \
802  { \
803  rlbox_detail_static_fail_because( \
804  detail::true_v<T_Lhs>, \
805  "C++ does not permit safe overloading of && and || operations as this " \
806  "affects the short circuiting behaviour of these operations. RLBox " \
807  "does let you use && and || with tainted in limited situations - when " \
808  "all arguments starting from the second are local variables. It does " \
809  "not allow it if arguments starting from the second are expressions.\n" \
810  "For example the following is not allowed\n" \
811  "\n" \
812  "tainted<bool, T_Sbx> a = true;\n" \
813  "auto r = a && true && sandbox.invoke_sandbox_function(getBool);\n" \
814  "\n" \
815  "However the following would be allowed\n" \
816  "tainted<bool, T_Sbx> a = true;\n" \
817  "auto b = true\n" \
818  "auto c = sandbox.invoke_sandbox_function(getBool);\n" \
819  "auto r = a && b && c;\n" \
820  "\n" \
821  "Note that these 2 programs are not identical. The first program may " \
822  "or may not call getBool, while second program always calls getBool"); \
823  return tainted<bool, T_Sbx>(false); \
824  } \
825  RLBOX_REQUIRE_SEMI_COLON
826 
827 BooleanBinaryOpWrappedRhs(&&);
828 BooleanBinaryOpWrappedRhs(||);
829 #undef BooleanBinaryOpWrappedRhs
830 
831 namespace tainted_detail {
832  template<typename T, typename T_Sbx>
833  using tainted_repr_t = detail::c_to_std_array_t<T>;
834 
835  template<typename T, typename T_Sbx>
836  using tainted_vol_repr_t =
837  detail::c_to_std_array_t<std::add_volatile_t<typename rlbox_sandbox<
838  T_Sbx>::template convert_to_sandbox_equivalent_nonclass_t<T>>>;
839 }
840 
845 template<typename T, typename T_Sbx>
846 class tainted : public tainted_base_impl<tainted, T, T_Sbx>
847 {
848  KEEP_CLASSES_FRIENDLY
849  KEEP_CAST_FRIENDLY
850 
851  // Classes recieve their own specialization
852  static_assert(
853  !std::is_class_v<T>,
854  "Missing specialization for class T. This error occurs for one "
855  "of 2 reasons.\n"
856  " 1) Make sure you have include a call rlbox_load_structs_from_library "
857  "for this library.\n"
858  " 2) Make sure you run (re-run) the struct-dump tool to list "
859  "all structs in use by your program.\n");
860 
861  static_assert(
862  detail::is_basic_type_v<T> || std::is_array_v<T>,
863  "Tainted types only support fundamental, enum, pointer, array and struct "
864  "types. Please file a bug if more support is needed.");
865 
866 private:
867  using T_ClassBase = tainted_base_impl<tainted, T, T_Sbx>;
868  using T_AppType = tainted_detail::tainted_repr_t<T, T_Sbx>;
869  using T_SandboxedType = tainted_detail::tainted_vol_repr_t<T, T_Sbx>;
870  T_AppType data;
871 
872  inline auto& get_raw_value_ref() noexcept { return data; }
873  inline auto& get_raw_value_ref() const noexcept { return data; }
874 
875  inline std::remove_cv_t<T_AppType> get_raw_value() const noexcept
876  {
877  return data;
878  }
879 
880  inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value(
881  rlbox_sandbox<T_Sbx>& sandbox) const
882  {
883  std::remove_cv_t<T_SandboxedType> ret;
884 
885  using namespace detail;
886  convert_type_non_class<T_Sbx,
887  adjust_type_direction::TO_SANDBOX,
888  adjust_type_context::SANDBOX>(
889  ret, data, nullptr /* example_unsandboxed_ptr */, &sandbox);
890  return ret;
891  };
892 
893  inline std::remove_cv_t<T_AppType> get_raw_value() noexcept
894  {
895  rlbox_detail_forward_to_const(get_raw_value, std::remove_cv_t<T_AppType>);
896  }
897 
898  inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value(
899  rlbox_sandbox<T_Sbx>& sandbox)
900  {
901  rlbox_detail_forward_to_const_a(
902  get_raw_sandbox_value, std::remove_cv_t<T_SandboxedType>, sandbox);
903  };
904 
905  inline const void* find_example_pointer_or_null() const noexcept
906  {
907  if constexpr (std::is_array_v<T>) {
908  auto& data_ref = get_raw_value_ref();
909 
910  for (size_t i = 0; i < std::extent_v<T>; i++) {
911  const void* ret = data[i].find_example_pointer_or_null();
912  if (ret != nullptr) {
913  return ret;
914  }
915  }
916  } else if constexpr (std::is_pointer_v<T> && !detail::is_func_ptr_v<T>) {
917  auto data = get_raw_value();
918  return data;
919  }
920  return nullptr;
921  }
922 
923  // Initializing with a pointer is dangerous and permitted only internally
924  template<typename T2 = T, RLBOX_ENABLE_IF(std::is_pointer_v<T2>)>
925  tainted(T2 val, const void* /* internal_tag */)
926  : data(val)
927  {
928  // Sanity check
929  static_assert(std::is_pointer_v<T>);
930  }
931 
932  template<typename T_Rhs>
933  static inline tainted<T, T_Sbx> internal_factory(T_Rhs&& rhs)
934  {
935  if constexpr (std::is_pointer_v<std::remove_reference_t<T_Rhs>>) {
936  const void* internal_tag = nullptr;
937  return tainted(std::forward<T_Rhs>(rhs), internal_tag);
938  } else {
939  return tainted(std::forward<T_Rhs>(rhs));
940  }
941  }
942 
943 public:
944  tainted() = default;
945  tainted(const tainted<T, T_Sbx>& p) = default;
946 
947  tainted(const tainted_volatile<T, T_Sbx>& p)
948  {
949  // Need to construct an example_unsandboxed_ptr for pointers or arrays of
950  // pointers. Since tainted_volatile is the type of data in sandbox memory,
951  // the address of data (&data) refers to a location in sandbox memory and
952  // can thus be the example_unsandboxed_ptr
953  const volatile void* p_data_ref = &p.get_sandbox_value_ref();
954  const void* example_unsandboxed_ptr = const_cast<const void*>(p_data_ref);
955  using namespace detail;
956  convert_type_non_class<T_Sbx,
957  adjust_type_direction::TO_APPLICATION,
958  adjust_type_context::EXAMPLE>(
959  get_raw_value_ref(),
960  p.get_sandbox_value_ref(),
961  example_unsandboxed_ptr,
962  nullptr /* sandbox_ptr */);
963  }
964 
965  // Initializing with a pointer is dangerous and permitted only internally
966  template<typename T2 = T, RLBOX_ENABLE_IF(std::is_pointer_v<T2>)>
967  tainted(T2 val)
968  : data(val)
969  {
970  rlbox_detail_static_fail_because(
971  std::is_pointer_v<T2>,
972  "Assignment of pointers is not safe as it could\n "
973  "1) Leak pointers from the appliction to the sandbox which may break "
974  "ASLR\n "
975  "2) Pass inaccessible pointers to the sandbox leading to crash\n "
976  "3) Break sandboxes that require pointers to be swizzled first\n "
977  "\n "
978  "Instead, if you want to pass in a pointer, do one of the following\n "
979  "1) Allocate with malloc_in_sandbox, and pass in a tainted pointer\n "
980  "2) For pointers that point to functions in the application, register "
981  "with sandbox.register_callback(\"foo\"), and pass in the registered "
982  "value\n "
983  "3) For pointers that point to functions in the sandbox, get the "
984  "address with get_sandbox_function_address(sandbox, foo), and pass in "
985  "the "
986  "address\n "
987  "4) For raw pointers, use assign_raw_pointer which performs required "
988  "safety checks\n ");
989  }
990 
991  tainted(
992  const sandbox_callback<
993  detail::function_ptr_t<T> // Need to ensure we never generate code that
994  // creates a sandbox_callback of a non function
995  ,
996  T_Sbx>&)
997  {
998  rlbox_detail_static_fail_because(
999  detail::true_v<T>,
1000  "RLBox does not support assigning sandbox_callback values to tainted "
1001  "types (i.e. types that live in application memory).\n"
1002  "If you still want to do this, consider changing your code to store the "
1003  "value in sandbox memory as follows. Convert\n\n"
1004  "sandbox_callback<T_Func, Sbx> cb = ...;\n"
1005  "tainted<T_Func, Sbx> foo = cb;\n\n"
1006  "to\n\n"
1007  "tainted<T_Func*, Sbx> foo_ptr = sandbox.malloc_in_sandbox<T_Func*>();\n"
1008  "*foo_ptr = cb;\n\n"
1009  "This would keep the assignment in sandbox memory");
1010  }
1011 
1012  tainted(const std::nullptr_t& arg)
1013  : data(arg)
1014  {
1015  static_assert(std::is_pointer_v<T>);
1016  }
1017 
1018  // We explicitly disable this constructor if it has one of the signatures
1019  // above, so that we give the above constructors a higher priority. We only
1020  // allow this for fundamental types as this is potentially unsafe for pointers
1021  // and structs
1022  template<typename T_Arg,
1023  RLBOX_ENABLE_IF(
1024  !detail::rlbox_is_wrapper_v<std::remove_reference_t<T_Arg>> &&
1025  detail::is_fundamental_or_enum_v<T> &&
1026  detail::is_fundamental_or_enum_v<std::remove_reference_t<T_Arg>>)>
1027  tainted(T_Arg&& arg)
1028  : data(std::forward<T_Arg>(arg))
1029  {}
1030 
1031  template<typename T_Rhs>
1032  void assign_raw_pointer(rlbox_sandbox<T_Sbx>& sandbox, T_Rhs val)
1033  {
1034  static_assert(std::is_pointer_v<T_Rhs>, "Must be a pointer");
1035  static_assert(std::is_assignable_v<T&, T_Rhs>,
1036  "Should assign pointers of compatible types.");
1037  // Maybe a function pointer, so we need to cast
1038  const void* cast_val = reinterpret_cast<const void*>(val);
1039  bool safe = sandbox.is_pointer_in_sandbox_memory(cast_val);
1040  detail::dynamic_check(
1041  safe,
1042  "Tried to assign a pointer that is not in the sandbox.\n "
1043  "This is not safe as it could\n "
1044  "1) Leak pointers from the appliction to the sandbox which may break "
1045  "ASLR\n "
1046  "2) Pass inaccessible pointers to the sandbox leading to crash\n "
1047  "3) Break sandboxes that require pointers to be swizzled first\n "
1048  "\n "
1049  "Instead, if you want to pass in a pointer, do one of the following\n "
1050  "1) Allocate with malloc_in_sandbox, and pass in a tainted pointer\n "
1051  "2) For pointers that point to functions in the application, register "
1052  "with sandbox.register_callback(\"foo\"), and pass in the registered "
1053  "value\n "
1054  "3) For pointers that point to functions in the sandbox, get the "
1055  "address with get_sandbox_function_address(sandbox, foo), and pass in "
1056  "the "
1057  "address\n ");
1058  data = val;
1059  }
1060 
1061  inline tainted_opaque<T, T_Sbx> to_opaque()
1062  {
1063  return *reinterpret_cast<tainted_opaque<T, T_Sbx>*>(this);
1064  }
1065 
1066  template<typename T_Dummy = void>
1067  operator bool() const
1068  {
1069  if_constexpr_named(cond1, std::is_pointer_v<T>)
1070  {
1071  // We return this without the tainted wrapper as the checking for null
1072  // doesn't really "induce" tainting in the application If the
1073  // application is checking this pointer for null, then it is robust to
1074  // this pointer being null or not null
1075  return get_raw_value() != nullptr;
1076  }
1077  else
1078  {
1079  auto unknownCase = !(cond1);
1080  rlbox_detail_static_fail_because(
1081  unknownCase,
1082  "Implicit conversion to bool is only permitted for pointer types. For "
1083  "other types, unwrap the tainted value with the copy_and_verify API "
1084  "and then perform the required checks");
1085  }
1086  }
1087 };
1088 
1089 template<typename T, typename T_Sbx>
1090 inline tainted<T, T_Sbx> from_opaque(tainted_opaque<T, T_Sbx> val)
1091 {
1092  return *reinterpret_cast<tainted<T, T_Sbx>*>(&val);
1093 }
1094 
1099 template<typename T, typename T_Sbx>
1100 class tainted_volatile : public tainted_base_impl<tainted_volatile, T, T_Sbx>
1101 {
1102  KEEP_CLASSES_FRIENDLY
1103  KEEP_CAST_FRIENDLY
1104 
1105  // Classes recieve their own specialization
1106  static_assert(
1107  !std::is_class_v<T>,
1108  "Missing specialization for class T. This error occurs for one "
1109  "of 2 reasons.\n"
1110  " 1) Make sure you have include a call rlbox_load_structs_from_library "
1111  "for this library.\n"
1112  " 2) Make sure you run (re-run) the struct-dump tool to list "
1113  "all structs in use by your program.\n");
1114 
1115  static_assert(
1116  detail::is_basic_type_v<T> || std::is_array_v<T>,
1117  "Tainted types only support fundamental, enum, pointer, array and struct "
1118  "types. Please file a bug if more support is needed.");
1119 
1120 private:
1122  using T_AppType = tainted_detail::tainted_repr_t<T, T_Sbx>;
1123  using T_SandboxedType = tainted_detail::tainted_vol_repr_t<T, T_Sbx>;
1124  T_SandboxedType data;
1125 
1126  inline auto& get_sandbox_value_ref() noexcept { return data; }
1127  inline auto& get_sandbox_value_ref() const noexcept { return data; }
1128 
1129  inline std::remove_cv_t<T_AppType> get_raw_value() const
1130  {
1131  std::remove_cv_t<T_AppType> ret;
1132  // Need to construct an example_unsandboxed_ptr for pointers or arrays of
1133  // pointers. Since tainted_volatile is the type of data in sandbox memory,
1134  // the address of data (&data) refers to a location in sandbox memory and
1135  // can thus be the example_unsandboxed_ptr
1136  const volatile void* data_ref = &data;
1137  const void* example_unsandboxed_ptr = const_cast<const void*>(data_ref);
1138  using namespace detail;
1139  convert_type_non_class<T_Sbx,
1140  adjust_type_direction::TO_APPLICATION,
1141  adjust_type_context::EXAMPLE>(
1142  ret, data, example_unsandboxed_ptr, nullptr /* sandbox_ptr */);
1143  return ret;
1144  }
1145 
1146  inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value()
1147  const noexcept
1148  {
1149  return data;
1150  };
1151 
1152  inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value(
1153  rlbox_sandbox<T_Sbx>& sandbox) const noexcept
1154  {
1155  return data;
1156  };
1157 
1158  inline std::remove_cv_t<T_AppType> get_raw_value()
1159  {
1160  rlbox_detail_forward_to_const(get_raw_value, std::remove_cv_t<T_AppType>);
1161  }
1162 
1163  inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value() noexcept
1164  {
1165  rlbox_detail_forward_to_const(get_raw_sandbox_value,
1166  std::remove_cv_t<T_SandboxedType>);
1167  };
1168 
1169  inline std::remove_cv_t<T_SandboxedType> get_raw_sandbox_value(
1170  rlbox_sandbox<T_Sbx>& sandbox) noexcept
1171  {
1172  rlbox_detail_forward_to_const(get_raw_sandbox_value,
1173  std::remove_cv_t<T_SandboxedType>);
1174  };
1175 
1176  tainted_volatile() = default;
1177  tainted_volatile(const tainted_volatile<T, T_Sbx>& p) = default;
1178 
1179 public:
1180  inline tainted<const T*, T_Sbx> operator&() const noexcept
1181  {
1182  auto ref =
1183  detail::remove_volatile_from_ptr_cast(&this->get_sandbox_value_ref());
1184  auto ref_cast = reinterpret_cast<const T*>(ref);
1185  auto ret = tainted<const T*, T_Sbx>::internal_factory(ref_cast);
1186  return ret;
1187  }
1188 
1189  inline tainted<T*, T_Sbx> operator&() noexcept
1190  {
1191  using T_Ret = tainted<T*, T_Sbx>;
1192  rlbox_detail_forward_to_const(operator&, T_Ret);
1193  }
1194 
1195  // Needed as the definition of unary & above shadows the base's binary &
1196  rlbox_detail_forward_binop_to_base(&, T_ClassBase);
1197 
1198  template<typename T_RhsRef>
1199  inline tainted_volatile<T, T_Sbx>& operator=(T_RhsRef&& val)
1200  {
1201  using T_Rhs = std::remove_reference_t<T_RhsRef>;
1202  using T_Rhs_El = std::remove_all_extents_t<T_Rhs>;
1203 
1204  // Need to construct an example_unsandboxed_ptr for pointers or arrays of
1205  // pointers. Since tainted_volatile is the type of data in sandbox memory,
1206  // the address of data (&data) refers to a location in sandbox memory and
1207  // can thus be the example_unsandboxed_ptr
1208  const volatile void* data_ref = &get_sandbox_value_ref();
1209  const void* example_unsandboxed_ptr = const_cast<const void*>(data_ref);
1210  // Some branches don't use this
1211  RLBOX_UNUSED(example_unsandboxed_ptr);
1212 
1213  if_constexpr_named(
1214  cond1, std::is_same_v<std::remove_const_t<T_Rhs>, std::nullptr_t>)
1215  {
1216  static_assert(std::is_pointer_v<T>,
1217  "Null pointer can only be assigned to pointers");
1218  // assign using an integer instead of nullptr, as the pointer field may be
1219  // represented as integer
1220  data = 0;
1221  }
1222  else if_constexpr_named(cond2, detail::rlbox_is_tainted_v<T_Rhs>)
1223  {
1224  using namespace detail;
1225  convert_type_non_class<T_Sbx,
1226  adjust_type_direction::TO_SANDBOX,
1227  adjust_type_context::EXAMPLE>(
1228  get_sandbox_value_ref(),
1229  val.get_raw_value_ref(),
1230  example_unsandboxed_ptr,
1231  nullptr /* sandbox_ptr */);
1232  }
1233  else if_constexpr_named(cond3, detail::rlbox_is_tainted_volatile_v<T_Rhs>)
1234  {
1235  using namespace detail;
1236  convert_type_non_class<T_Sbx,
1237  adjust_type_direction::NO_CHANGE,
1238  adjust_type_context::EXAMPLE>(
1239  get_sandbox_value_ref(),
1240  val.get_sandbox_value_ref(),
1241  example_unsandboxed_ptr,
1242  nullptr /* sandbox_ptr */);
1243  }
1244  else if_constexpr_named(cond4, detail::rlbox_is_sandbox_callback_v<T_Rhs>)
1245  {
1246  using T_RhsFunc = detail::rlbox_remove_wrapper_t<T_Rhs>;
1247 
1248  // need to perform some typechecking to ensure we are assigning compatible
1249  // function pointer types only
1250  if_constexpr_named(subcond1, !std::is_assignable_v<T&, T_RhsFunc>)
1251  {
1252  rlbox_detail_static_fail_because(
1253  subcond1,
1254  "Trying to assign function pointer to field of incompatible types");
1255  }
1256  else
1257  {
1258  // Need to reinterpret_cast as the representation of the signature of a
1259  // callback uses the machine model of the sandbox, while the field uses
1260  // that of the application. But we have already checked above that this
1261  // is safe.
1262  auto func = val.get_raw_sandbox_value();
1263  using T_Cast = std::remove_volatile_t<T_SandboxedType>;
1264  get_sandbox_value_ref() = (T_Cast)func;
1265  }
1266  }
1267  else if_constexpr_named(
1268  cond5,
1269  detail::is_fundamental_or_enum_v<T> ||
1270  (std::is_array_v<T> && !std::is_pointer_v<T_Rhs_El>))
1271  {
1272  detail::convert_type_fundamental_or_array(get_sandbox_value_ref(), val);
1273  }
1274  else if_constexpr_named(
1275  cond6, std::is_pointer_v<T_Rhs> || std::is_pointer_v<T_Rhs_El>)
1276  {
1277  rlbox_detail_static_fail_because(
1278  cond6,
1279  "Assignment of pointers is not safe as it could\n "
1280  "1) Leak pointers from the appliction to the sandbox which may break "
1281  "ASLR\n "
1282  "2) Pass inaccessible pointers to the sandbox leading to crash\n "
1283  "3) Break sandboxes that require pointers to be swizzled first\n "
1284  "\n "
1285  "Instead, if you want to pass in a pointer, do one of the following\n "
1286  "1) Allocate with malloc_in_sandbox, and pass in a tainted pointer\n "
1287  "2) For pointers that point to functions in the application, register "
1288  "with sandbox.register_callback(\"foo\"), and pass in the registered "
1289  "value\n "
1290  "3) For pointers that point to functions in the sandbox, get the "
1291  "address with get_sandbox_function_address(sandbox, foo), and pass in "
1292  "the "
1293  "address\n "
1294  "4) For raw pointers, use assign_raw_pointer which performs required "
1295  "safety checks\n ");
1296  }
1297  else
1298  {
1299  auto unknownCase =
1300  !(cond1 || cond2 || cond3 || cond4 || cond5 /* || cond6 */);
1301  rlbox_detail_static_fail_because(
1302  unknownCase, "Assignment of the given type of value is not supported");
1303  }
1304 
1305  return *this;
1306  }
1307 
1308  template<typename T_Rhs>
1309  void assign_raw_pointer(rlbox_sandbox<T_Sbx>& sandbox, T_Rhs val)
1310  {
1311  static_assert(std::is_pointer_v<T_Rhs>, "Must be a pointer");
1312  static_assert(std::is_assignable_v<T&, T_Rhs>,
1313  "Should assign pointers of compatible types.");
1314  // Maybe a function pointer, so we need to cast
1315  const void* cast_val = reinterpret_cast<const void*>(val);
1316  bool safe = sandbox.is_pointer_in_sandbox_memory(cast_val);
1317  detail::dynamic_check(
1318  safe,
1319  "Tried to assign a pointer that is not in the sandbox.\n "
1320  "This is not safe as it could\n "
1321  "1) Leak pointers from the appliction to the sandbox which may break "
1322  "ASLR\n "
1323  "2) Pass inaccessible pointers to the sandbox leading to crash\n "
1324  "3) Break sandboxes that require pointers to be swizzled first\n "
1325  "\n "
1326  "Instead, if you want to pass in a pointer, do one of the following\n "
1327  "1) Allocate with malloc_in_sandbox, and pass in a tainted pointer\n "
1328  "2) For pointers that point to functions in the application, register "
1329  "with sandbox.register_callback(\"foo\"), and pass in the registered "
1330  "value\n "
1331  "3) For pointers that point to functions in the sandbox, get the "
1332  "address with get_sandbox_function_address(sandbox, foo), and pass in "
1333  "the "
1334  "address\n ");
1335  get_sandbox_value_ref() =
1336  sandbox.template get_sandboxed_pointer<T_Rhs>(cast_val);
1337  }
1338 
1339  template<typename T_Dummy = void>
1340  operator bool() const
1341  {
1342  rlbox_detail_static_fail_because(
1343  detail::true_v<T_Dummy>,
1344  "Cannot apply implicit conversion to bool on values that are located in "
1345  "sandbox memory. This error occurs if you compare a dereferenced value "
1346  "such as the code shown below\n\n"
1347  "tainted<int**> a = ...;\n"
1348  "assert(*a);\n\n"
1349  "Instead you can write this code as \n"
1350  "tainted<int*> temp = *a;\n"
1351  "assert(temp);\n");
1352  return false;
1353  }
1354 };
1355 
1356 }
Encapsulation for sandboxes.
Definition: rlbox_sandbox.hpp:95
static bool is_in_same_sandbox(const void *p1, const void *p2)
Check if two pointers are in the same sandbox. For the null-sandbox, this always returns true.
Definition: rlbox_sandbox.hpp:584
bool is_pointer_in_sandbox_memory(const void *p)
Check if the pointer points to this sandbox's memory. For the null-sandbox, this always returns true.
Definition: rlbox_sandbox.hpp:598
Definition: rlbox.hpp:28
auto UNSAFE_unverified()
Unwrap a tainted value without verification. This is an unsafe operation and should be used with care...
Definition: rlbox.hpp:43
auto copy_and_verify_buffer_address(T_Func verifier, std::size_t size)
Copy a tainted pointer to a buffer from sandbox and verify the address.
Definition: rlbox.hpp:724
auto copy_and_verify(T_Func verifier) const
Copy tainted value from sandbox and verify it.
Definition: rlbox.hpp:503
auto copy_and_verify_range(T_Func verifier, std::size_t count) const
Copy a range of tainted values from sandbox and verify them.
Definition: rlbox.hpp:638
auto copy_and_verify_string(T_Func verifier) const
Copy a tainted string from sandbox and verify it.
Definition: rlbox.hpp:662
rlbox_detail_member_and_const(template< size_t N > inline auto unverified_safe_because(const char(&reason)[N]), { RLBOX_UNUSED(reason);static_assert(!std::is_pointer_v< T >, "unverified_safe_because does not support pointers. Use " "unverified_safe_pointer_because.");return UNSAFE_unverified();})
Unwrap a tainted value without verification. This function should be used when unwrapping is safe.
auto copy_and_verify_address(T_Func verifier)
Copy a tainted pointer from sandbox and verify the address.
Definition: rlbox.hpp:701
auto UNSAFE_sandboxed(rlbox_sandbox< T_Sbx > &sandbox)
Like UNSAFE_unverified, but get the underlying sandbox representation.
Definition: rlbox.hpp:54
Tainted volatile values are like tainted values but still point to sandbox memory....
Definition: rlbox.hpp:1101
Definition: rlbox_types.hpp:22