RLBox
rlbox_sandbox.hpp
1 #pragma once
2 // IWYU pragma: private, include "rlbox.hpp"
3 // IWYU pragma: friend "rlbox_.*\.hpp"
4 
5 #include <algorithm>
6 #include <atomic>
7 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
8 # include <chrono>
9 #endif
10 #include <cstdlib>
11 #include <limits>
12 #include <map>
13 #include <mutex>
14 #ifndef RLBOX_USE_CUSTOM_SHARED_LOCK
15 # include <shared_mutex>
16 #endif
17 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
18 # include <sstream>
19 # include <string>
20 #endif
21 #include <stdint.h>
22 #include <type_traits>
23 #include <utility>
24 #include <vector>
25 
26 #include "rlbox_conversion.hpp"
27 #include "rlbox_helpers.hpp"
28 #include "rlbox_stdlib_polyfill.hpp"
29 #include "rlbox_struct_support.hpp"
30 #include "rlbox_type_traits.hpp"
31 #include "rlbox_wrapper_traits.hpp"
32 
33 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
34 using namespace std::chrono;
35 #endif
36 
37 namespace rlbox {
38 
39 namespace convert_fn_ptr_to_sandbox_equivalent_detail {
40  template<typename T, typename T_Sbx>
41  using conv = ::rlbox::detail::convert_to_sandbox_equivalent_t<T, T_Sbx>;
42 
43  template<typename T_Ret, typename... T_Args>
44  using T_Func = T_Ret (*)(T_Args...);
45 
46  template<typename T_Sbx, typename T_Ret, typename... T_Args>
47  T_Func<conv<T_Ret, T_Sbx>, conv<T_Args, T_Sbx>...> helper(
48  T_Ret (*)(T_Args...));
49 }
50 
51 #if defined(RLBOX_MEASURE_TRANSITION_TIMES) || \
52  defined(RLBOX_TRANSITION_ACTION_OUT) || defined(RLBOX_TRANSITION_ACTION_IN)
53 enum class rlbox_transition
54 {
55  INVOKE,
56  CALLBACK
57 };
58 #endif
59 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
60 struct rlbox_transition_timing
61 {
62  rlbox_transition invoke;
63  const char* name;
64  void* ptr;
65  int64_t time;
66 
67  std::string to_string()
68  {
69  std::ostringstream ret;
70  if (invoke == rlbox_transition::INVOKE) {
71  ret << name;
72  } else {
73  ret << "Callback " << ptr;
74  }
75  ret << " : " << time << "\n";
76 
77  return ret.str();
78  }
79 };
80 #endif
81 
82 #ifndef RLBOX_SINGLE_THREADED_INVOCATIONS
83 # error \
84  "RLBox does not yet support threading. Please define RLBOX_SINGLE_THREADED_INVOCATIONS prior to including RLBox and ensure you are only using it from a single thread. If threading is required, please file a bug."
85 #endif
86 
93 template<typename T_Sbx>
94 class rlbox_sandbox : protected T_Sbx
95 {
96  KEEP_CLASSES_FRIENDLY
97 
98 private:
99 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
100  std::vector<rlbox_transition_timing> transition_times;
101 #endif
102 
103  static inline RLBOX_SHARED_LOCK(sandbox_list_lock);
104  // The actual type of the vector is std::vector<rlbox_sandbox<T_Sbx>*>
105  // However clang 5, 6 have bugs where compilation seg-faults on this type
106  // So we just use this std::vector<void*>
107  static inline std::vector<void*> sandbox_list;
108 
109  RLBOX_SHARED_LOCK(func_ptr_cache_lock);
110  std::map<std::string, void*> func_ptr_map;
111 
113 
114  // This variable tracks of the sandbox has already been created/destroyed.
115  // APIs in this class should be called only when the sandbox is created.
116  // However, it is expensive to check in APIs such as invoke or in the callback
117  // interceptor. What's more, there could be time of check time of use issues
118  // in the checks as well.
119  // In general, we leave it up to the user to ensure these APIs are never
120  // called prior to sandbox construction or after destruction. We perform some
121  // conservative sanity checks, where they would not add too much overhead.
122  enum class Sandbox_Status
123  {
124  NOT_CREATED,
125  INITIALIZING,
126  CREATED,
127  CLEANING_UP
128  };
129  std::atomic<Sandbox_Status> sandbox_created = Sandbox_Status::NOT_CREATED;
130 
131  std::mutex callback_lock;
132  std::vector<void*> callback_keys;
133 
134  void* transition_state = nullptr;
135 
136  template<typename T>
137  using convert_fn_ptr_to_sandbox_equivalent_t =
138  decltype(::rlbox::convert_fn_ptr_to_sandbox_equivalent_detail::helper<
139  T_Sbx>(std::declval<T>()));
140 
141  template<typename T>
142  inline constexpr void check_invoke_param_type_is_ok()
143  {
144  using T_NoRef = std::remove_reference_t<T>;
145 
146  if_constexpr_named(cond1, detail::rlbox_is_wrapper_v<T_NoRef>)
147  {
148  if_constexpr_named(
149  subcond1,
150  !std::is_same_v<T_Sbx, detail::rlbox_get_wrapper_sandbox_t<T_NoRef>>)
151  {
152  rlbox_detail_static_fail_because(
153  cond1 && subcond1,
154  "Mixing tainted data from a different sandbox types. This could "
155  "happen due to couple of different reasons.\n"
156  "1. You are using 2 sandbox types for example'rlbox_noop_sandbox' "
157  "and 'rlbox_lucet_sandbox', and are passing tainted data from one "
158  "sandbox as parameters into a function call to the other sandbox. "
159  "This is not allowed, unwrap the tainted data with copy_and_verify "
160  "or other unwrapping APIs first.\n"
161  "2. You have inadvertantly forgotten to set/remove "
162  "RLBOX_USE_STATIC_CALLS depending on the sandbox type. Some sandbox "
163  "types like rlbox_noop_sandbox require this to be set to a given "
164  "value, while other types like rlbox_lucet_sandbox, require this not "
165  "to be set.");
166  }
167  }
168  else if_constexpr_named(cond2,
169  std::is_null_pointer_v<T_NoRef> ||
170  detail::is_fundamental_or_enum_v<T_NoRef>)
171  {}
172  else
173  {
174  constexpr auto unknownCase = !(cond1 || cond2);
175  rlbox_detail_static_fail_because(
176  unknownCase,
177  "Arguments to a sandbox function call should be primitives or wrapped "
178  "types like tainted, callbacks etc.");
179  }
180  }
181 
182  template<typename T>
183  inline auto invoke_process_param(T&& param)
184  {
185  check_invoke_param_type_is_ok<T>();
186 
187  using T_NoRef = std::remove_reference_t<T>;
188 
189  if constexpr (detail::rlbox_is_tainted_opaque_v<T_NoRef>) {
190  auto ret = from_opaque(param);
191  return ret.UNSAFE_sandboxed(*this);
192  } else if constexpr (detail::rlbox_is_wrapper_v<T_NoRef>) {
193  return param.UNSAFE_sandboxed(*this);
194  } else if constexpr (std::is_null_pointer_v<T_NoRef>) {
195  tainted<void*, T_Sbx> ret = nullptr;
196  return ret.UNSAFE_sandboxed(*this);
197  } else if constexpr (detail::is_fundamental_or_enum_v<T_NoRef>) {
198  // For unwrapped primitives, assign to a tainted var and then unwrap so
199  // that we adjust for machine model
200  tainted<T_NoRef, T_Sbx> ret = param;
201  return ret.UNSAFE_sandboxed(*this);
202  } else {
203  rlbox_detail_static_fail_because(detail::true_v<T_NoRef>, "Unknown case");
204  }
205  }
206 
207  template<typename T, typename T_Arg>
208  inline tainted<T, T_Sbx> sandbox_callback_intercept_convert_param(
209  rlbox_sandbox<T_Sbx>& sandbox,
210  const T_Arg& arg)
211  {
212  tainted<T, T_Sbx> ret;
213  using namespace detail;
214  convert_type<T_Sbx,
215  adjust_type_direction::TO_APPLICATION,
216  adjust_type_context::SANDBOX>(
217  ret.get_raw_value_ref(),
218  arg,
219  nullptr /* example_unsandboxed_ptr */,
220  &sandbox);
221  return ret;
222  }
223 
224  template<typename T_Ret, typename... T_Args>
225  static detail::convert_to_sandbox_equivalent_t<T_Ret, T_Sbx>
226  sandbox_callback_interceptor(
227  detail::convert_to_sandbox_equivalent_t<T_Args, T_Sbx>... args)
228  {
229  std::pair<T_Sbx*, void*> context =
230  T_Sbx::impl_get_executed_callback_sandbox_and_key();
231  auto& sandbox = *(reinterpret_cast<rlbox_sandbox<T_Sbx>*>(context.first));
232  auto key = context.second;
233 
234  using T_Func_Ret =
235  std::conditional_t<std::is_void_v<T_Ret>, void, tainted<T_Ret, T_Sbx>>;
236  using T_Func =
237  T_Func_Ret (*)(rlbox_sandbox<T_Sbx>&, tainted<T_Args, T_Sbx>...);
238  auto target_fn_ptr = reinterpret_cast<T_Func>(key);
239 
240 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
241  high_resolution_clock::time_point enter_time = high_resolution_clock::now();
242  auto on_exit = rlbox::detail::make_scope_exit([&] {
243  auto exit_time = high_resolution_clock::now();
244  int64_t ns = duration_cast<nanoseconds>(exit_time - enter_time).count();
245  sandbox.transition_times.push_back(
246  rlbox_transition_timing{ rlbox_transition::CALLBACK,
247  nullptr /* func_name */,
248  key /* func_ptr */,
249  ns });
250  });
251 #endif
252 #ifdef RLBOX_TRANSITION_ACTION_OUT
253  RLBOX_TRANSITION_ACTION_OUT(rlbox_transition::CALLBACK,
254  nullptr /* func_name */,
255  key /* func_ptr */,
256  sandbox.transition_state);
257 #endif
258 #ifdef RLBOX_TRANSITION_ACTION_IN
259  auto on_exit_transition = rlbox::detail::make_scope_exit([&] {
260  RLBOX_TRANSITION_ACTION_IN(rlbox_transition::CALLBACK,
261  nullptr /* func_name */,
262  key /* func_ptr */,
263  sandbox.transition_state);
264  });
265 #endif
266  if constexpr (std::is_void_v<T_Func_Ret>) {
267  (*target_fn_ptr)(
268  sandbox,
269  sandbox.template sandbox_callback_intercept_convert_param<T_Args>(
270  sandbox, args)...);
271  return;
272  } else {
273  auto tainted_ret = (*target_fn_ptr)(
274  sandbox,
275  sandbox.template sandbox_callback_intercept_convert_param<T_Args>(
276  sandbox, args)...);
277 
278  using namespace detail;
279  convert_to_sandbox_equivalent_t<T_Ret, T_Sbx> ret;
280  convert_type<T_Sbx,
281  adjust_type_direction::TO_SANDBOX,
282  adjust_type_context::SANDBOX>(
283  ret,
284  tainted_ret.get_raw_value_ref(),
285  nullptr /* example_unsandboxed_ptr */,
286  &sandbox);
287  return ret;
288  }
289  }
290 
295  template<typename T_Ret, typename... T_Args>
296  inline void unregister_callback(void* key)
297  {
298  // Silently swallowing the failure is better here as RAII types may try to
299  // cleanup callbacks after sandbox destruction
300  if (sandbox_created.load() != Sandbox_Status::CREATED) {
301  return;
302  }
303 
304  this->template impl_unregister_callback<
305  detail::convert_to_sandbox_equivalent_t<T_Ret, T_Sbx>,
306  detail::convert_to_sandbox_equivalent_t<T_Args, T_Sbx>...>(key);
307 
308  std::lock_guard<std::mutex> lock(callback_lock);
309  auto el_ref = std::find(callback_keys.begin(), callback_keys.end(), key);
310  detail::dynamic_check(
311  el_ref != callback_keys.end(),
312  "Unexpected state. Unregistering a callback that was never registered.");
313  callback_keys.erase(el_ref);
314  }
315 
316  static T_Sbx* find_sandbox_from_example(const void* example_sandbox_ptr)
317  {
318  detail::dynamic_check(
319  example_sandbox_ptr != nullptr,
320  "Internal error: received a null example pointer. Please file a bug.");
321 
322  RLBOX_ACQUIRE_SHARED_GUARD(lock, sandbox_list_lock);
323  for (auto sandbox_v : sandbox_list) {
324  auto sandbox = reinterpret_cast<rlbox_sandbox<T_Sbx>*>(sandbox_v);
325  if (sandbox->is_pointer_in_sandbox_memory(example_sandbox_ptr)) {
326  return sandbox;
327  }
328  }
329 
330  return nullptr;
331  }
332 
333 public:
340 
341  /***** Function to adjust for custom machine models *****/
342 
343  template<typename T>
344  using convert_to_sandbox_equivalent_nonclass_t =
345  detail::convert_base_types_t<T,
346  typename T_Sbx::T_ShortType,
347  typename T_Sbx::T_IntType,
348  typename T_Sbx::T_LongType,
349  typename T_Sbx::T_LongLongType,
350  typename T_Sbx::T_PointerType>;
351 
352  T_Sbx* get_sandbox_impl() { return this; }
353 
360  template<typename... T_Args>
361  inline auto create_sandbox(T_Args... args)
362  {
363 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
364  // Warm up the timer. The first call is always slow (at least on the test
365  // platform)
366  for (int i = 0; i < 10; i++) {
367  auto val = high_resolution_clock::now();
368  RLBOX_UNUSED(val);
369  }
370 #endif
371  auto expected = Sandbox_Status::NOT_CREATED;
372  bool success = sandbox_created.compare_exchange_strong(
373  expected, Sandbox_Status::INITIALIZING /* desired */);
374  detail::dynamic_check(
375  success,
376  "create_sandbox called when sandbox already created/is being "
377  "created concurrently");
378 
379  return detail::return_first_result(
380  [&]() {
381  return this->impl_create_sandbox(std::forward<T_Args>(args)...);
382  },
383  [&]() {
384  sandbox_created.store(Sandbox_Status::CREATED);
385  RLBOX_ACQUIRE_UNIQUE_GUARD(lock, sandbox_list_lock);
386  sandbox_list.push_back(this);
387  });
388  }
389 
393  inline auto destroy_sandbox()
394  {
395  auto expected = Sandbox_Status::CREATED;
396  bool success = sandbox_created.compare_exchange_strong(
397  expected, Sandbox_Status::CLEANING_UP /* desired */);
398 
399  detail::dynamic_check(
400  success,
401  "destroy_sandbox called without sandbox creation/is being "
402  "destroyed concurrently");
403 
404  {
405  RLBOX_ACQUIRE_UNIQUE_GUARD(lock, sandbox_list_lock);
406  auto el_ref = std::find(sandbox_list.begin(), sandbox_list.end(), this);
407  detail::dynamic_check(
408  el_ref != sandbox_list.end(),
409  "Unexpected state. Destroying a sandbox that was never initialized.");
410  sandbox_list.erase(el_ref);
411  }
412 
413  sandbox_created.store(Sandbox_Status::NOT_CREATED);
414  return this->impl_destroy_sandbox();
415  }
416 
417  template<typename T>
418  inline T get_unsandboxed_pointer(
419  convert_to_sandbox_equivalent_nonclass_t<T> p) const
420  {
421  static_assert(std::is_pointer_v<T>);
422  if (p == 0) {
423  return nullptr;
424  }
425  auto ret = this->template impl_get_unsandboxed_pointer<T>(p);
426  return reinterpret_cast<T>(ret);
427  }
428 
429  template<typename T>
430  inline convert_to_sandbox_equivalent_nonclass_t<T> get_sandboxed_pointer(
431  const void* p) const
432  {
433  static_assert(std::is_pointer_v<T>);
434  if (p == nullptr) {
435  return 0;
436  }
437  return this->template impl_get_sandboxed_pointer<T>(p);
438  }
439 
440  template<typename T>
441  static inline T get_unsandboxed_pointer_no_ctx(
442  convert_to_sandbox_equivalent_nonclass_t<T> p,
443  const void* example_unsandboxed_ptr)
444  {
445  static_assert(std::is_pointer_v<T>);
446  if (p == 0) {
447  return nullptr;
448  }
449  auto ret = T_Sbx::template impl_get_unsandboxed_pointer_no_ctx<T>(
450  p, example_unsandboxed_ptr, find_sandbox_from_example);
451  return reinterpret_cast<T>(ret);
452  }
453 
454  template<typename T>
455  static inline convert_to_sandbox_equivalent_nonclass_t<T>
456  get_sandboxed_pointer_no_ctx(const void* p,
457  const void* example_unsandboxed_ptr)
458  {
459  static_assert(std::is_pointer_v<T>);
460  if (p == nullptr) {
461  return 0;
462  }
463  return T_Sbx::template impl_get_sandboxed_pointer_no_ctx<T>(
464  p, example_unsandboxed_ptr, find_sandbox_from_example);
465  }
466 
477  template<typename T>
479  {
480  const uint32_t defaultCount = 1;
481  return malloc_in_sandbox<T>(defaultCount);
482  }
483 
496  template<typename T>
497  inline tainted<T*, T_Sbx> malloc_in_sandbox(uint32_t count)
498  {
499  // Silently swallowing the failure is better here as RAII types may try to
500  // malloc after sandbox destruction
501  if (sandbox_created.load() != Sandbox_Status::CREATED) {
502  return tainted<T*, T_Sbx>::internal_factory(nullptr);
503  }
504 
505  detail::dynamic_check(count != 0, "Malloc tried to allocate 0 bytes");
506  if constexpr (sizeof(T) >= std::numeric_limits<uint32_t>::max()) {
507  rlbox_detail_static_fail_because(sizeof(T) >=
508  std::numeric_limits<uint32_t>::max(),
509  "Tried to allocate an object over 4GB.");
510  }
511  auto total_size = static_cast<uint64_t>(sizeof(T)) * count;
512  if constexpr (sizeof(size_t) == 4) {
513  // On a 32-bit platform, we need to make sure that total_size is not >=4GB
514  detail::dynamic_check(total_size < std::numeric_limits<uint32_t>::max(),
515  "Tried to allocate memory over 4GB");
516  } else if constexpr (sizeof(size_t) != 8) {
517  // Double check we are on a 64-bit platform
518  // Note for static checks we need to have some dependence on T, so adding
519  // a dummy
520  constexpr bool dummy = sizeof(T) >= 0;
521  rlbox_detail_static_fail_because(dummy && sizeof(size_t) != 8,
522  "Expected 32 or 64 bit platform.");
523  }
524  auto ptr_in_sandbox = this->impl_malloc_in_sandbox(total_size);
525  auto ptr = get_unsandboxed_pointer<T*>(ptr_in_sandbox);
526  if (!ptr) {
527  return tainted<T*, T_Sbx>(nullptr);
528  }
529  detail::dynamic_check(is_pointer_in_sandbox_memory(ptr),
530  "Malloc returned pointer outside the sandbox memory");
531  auto ptr_end = reinterpret_cast<uintptr_t>(ptr + (count - 1));
532  detail::dynamic_check(
533  is_in_same_sandbox(ptr, reinterpret_cast<void*>(ptr_end)),
534  "Malloc returned a pointer whose range goes beyond sandbox memory");
535  auto cast_ptr = reinterpret_cast<T*>(ptr);
536  return tainted<T*, T_Sbx>::internal_factory(cast_ptr);
537  }
538 
544  template<typename T>
546  {
547  // Silently swallowing the failure is better here as RAII types may try to
548  // free after sandbox destruction
549  if (sandbox_created.load() != Sandbox_Status::CREATED) {
550  return;
551  }
552 
553  this->impl_free_in_sandbox(ptr.get_raw_sandbox_value(*this));
554  }
555 
561  template<typename T>
563  {
564  tainted<T, T_Sbx> ptr = ptr_ref;
565  free_in_sandbox(ptr);
566  }
567 
573  template<typename T>
575  {
576  tainted<T, T_Sbx> ptr = from_opaque(ptr_opaque);
577  free_in_sandbox(ptr);
578  }
579 
584  static inline bool is_in_same_sandbox(const void* p1, const void* p2)
585  {
586  const size_t num_args = detail::func_arg_nums_v<decltype(T_Sbx::impl_is_in_same_sandbox)>;
587  if constexpr (num_args == 2) {
588  return T_Sbx::impl_is_in_same_sandbox(p1, p2);
589  } else {
590  return T_Sbx::impl_is_in_same_sandbox(p1, p2, find_sandbox_from_example);
591  }
592  }
593 
598  inline bool is_pointer_in_sandbox_memory(const void* p)
599  {
600  return this->impl_is_pointer_in_sandbox_memory(p);
601  }
602 
607  inline bool is_pointer_in_app_memory(const void* p)
608  {
609  return this->impl_is_pointer_in_app_memory(p);
610  }
611 
612  inline size_t get_total_memory() { return this->impl_get_total_memory(); }
613 
614  inline void* get_memory_location()
615  {
616  return this->impl_get_memory_location();
617  }
618 
619  void* get_transition_state() { return transition_state; }
620 
621  void set_transition_state(void* new_state) { transition_state = new_state; }
622 
632  template<typename T>
634  size_t num,
635  bool& success)
636  {
637  auto ret = this->impl_grant_access(src, num, success);
639  }
640 
650  template<typename T>
652  size_t num,
653  bool& success)
654  {
655  auto ret =
656  this->impl_deny_access(src.INTERNAL_unverified_safe(), num, success);
657  return ret;
658  }
659 
660  void* lookup_symbol(const char* func_name)
661  {
662  {
663  RLBOX_ACQUIRE_SHARED_GUARD(lock, func_ptr_cache_lock);
664 
665  auto func_ptr_ref = func_ptr_map.find(func_name);
666  if (func_ptr_ref != func_ptr_map.end()) {
667  return func_ptr_ref->second;
668  }
669  }
670 
671  void* func_ptr = this->impl_lookup_symbol(func_name);
672  RLBOX_ACQUIRE_UNIQUE_GUARD(lock, func_ptr_cache_lock);
673  func_ptr_map[func_name] = func_ptr;
674  return func_ptr;
675  }
676 
677  // this is an internal function invoked from macros, so it has be public
678  template<typename T, typename... T_Args>
679  inline auto INTERNAL_invoke_with_func_name(const char* func_name,
680  T_Args&&... params)
681  {
682  return INTERNAL_invoke_with_func_ptr<T, T_Args...>(
683  func_name, lookup_symbol(func_name), std::forward<T_Args>(params)...);
684  }
685 
686  // this is an internal function invoked from macros, so it has be public
687  // Explicitly don't use inline on this, as this adds a lot of instructions
688  // prior to function call. What's more, by not inlining, different function
689  // calls with the same signature can share the same code segments for
690  // sandboxed function execution in the binary
691  template<typename T, typename... T_Args>
692  auto INTERNAL_invoke_with_func_ptr(const char* func_name,
693  void* func_ptr,
694  T_Args&&... params)
695  {
696  // unused in some paths
697  RLBOX_UNUSED(func_name);
698 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
699  auto enter_time = high_resolution_clock::now();
700  auto on_exit = rlbox::detail::make_scope_exit([&] {
701  auto exit_time = high_resolution_clock::now();
702  int64_t ns = duration_cast<nanoseconds>(exit_time - enter_time).count();
703  transition_times.push_back(rlbox_transition_timing{
704  rlbox_transition::INVOKE, func_name, func_ptr, ns });
705  });
706 #endif
707 #ifdef RLBOX_TRANSITION_ACTION_IN
708  RLBOX_TRANSITION_ACTION_IN(
709  rlbox_transition::INVOKE, func_name, func_ptr, transition_state);
710 #endif
711 #ifdef RLBOX_TRANSITION_ACTION_OUT
712  auto on_exit_transition = rlbox::detail::make_scope_exit([&] {
713  RLBOX_TRANSITION_ACTION_OUT(
714  rlbox_transition::INVOKE, func_name, func_ptr, transition_state);
715  });
716 #endif
717  (check_invoke_param_type_is_ok<T_Args>(), ...);
718 
719  static_assert(
720  rlbox::detail::polyfill::is_invocable_v<
721  T,
722  detail::rlbox_remove_wrapper_t<std::remove_reference_t<T_Args>>...>,
723  "Mismatched arguments types for function");
724 
725  using T_Result = rlbox::detail::polyfill::invoke_result_t<
726  T,
727  detail::rlbox_remove_wrapper_t<std::remove_reference_t<T_Args>>...>;
728 
729  using T_Converted =
730  std::remove_pointer_t<convert_fn_ptr_to_sandbox_equivalent_t<T*>>;
731 
732  if constexpr (std::is_void_v<T_Result>) {
733  this->template impl_invoke_with_func_ptr<T>(
734  reinterpret_cast<T_Converted*>(func_ptr),
735  invoke_process_param(params)...);
736  return;
737  } else {
738  auto raw_result = this->template impl_invoke_with_func_ptr<T>(
739  reinterpret_cast<T_Converted*>(func_ptr),
740  invoke_process_param(params)...);
741  tainted<T_Result, T_Sbx> wrapped_result;
742  using namespace detail;
743  convert_type<T_Sbx,
744  adjust_type_direction::TO_APPLICATION,
745  adjust_type_context::SANDBOX>(
746  wrapped_result.get_raw_value_ref(),
747  raw_result,
748  nullptr /* example_unsandboxed_ptr */,
749  this /* sandbox_ptr */);
750  return wrapped_result;
751  }
752  }
753 
754  // Useful in the porting stage to temporarily allow non tainted pointers to go
755  // through. This will only ever work in the rlbox_noop_sandbox. Any sandbox
756  // that actually enforces isolation will crash here.
757  template<typename T2>
758  tainted<T2, T_Sbx> UNSAFE_accept_pointer(T2 ptr)
759  {
760  static_assert(std::is_pointer_v<T2>,
761  "UNSAFE_accept_pointer expects a pointer param");
762  tainted<T2, T_Sbx> ret;
763  ret.assign_raw_pointer(*this, ptr);
764  return ret;
765  }
766 
767  template<typename T_Ret, typename... T_Args>
768  using T_Cb_no_wrap = detail::rlbox_remove_wrapper_t<T_Ret>(
769  detail::rlbox_remove_wrapper_t<T_Args>...);
770 
771  template<typename T_Ret>
772  sandbox_callback<T_Cb_no_wrap<T_Ret>*, T_Sbx> register_callback(T_Ret (*)())
773  {
774  rlbox_detail_static_fail_because(
775  detail::true_v<T_Ret>,
776  "Modify the callback to change the first parameter to a sandbox."
777  "For instance if a callback has type\n\n"
778  "int foo() {...}\n\n"
779  "Change this to \n\n"
780  "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox) {...}\n");
781 
782  // this is never executed, but we need it for the function to type-check
783  std::abort();
784  }
785 
798  template<typename T_RL, typename T_Ret, typename... T_Args>
799  sandbox_callback<T_Cb_no_wrap<T_Ret, T_Args...>*, T_Sbx> register_callback(
800  T_Ret (*func_ptr)(T_RL, T_Args...))
801  {
802  // Some branches don't use the param
803  RLBOX_UNUSED(func_ptr);
804 
805  if_constexpr_named(cond1, !std::is_same_v<T_RL, rlbox_sandbox<T_Sbx>&>)
806  {
807  rlbox_detail_static_fail_because(
808  cond1,
809  "Modify the callback to change the first parameter to a sandbox."
810  "For instance if a callback has type\n\n"
811  "int foo(int a, int b) {...}\n\n"
812  "Change this to \n\n"
813  "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox,"
814  "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
815  }
816  else if_constexpr_named(
817  cond2, !(detail::rlbox_is_tainted_or_opaque_v<T_Args> && ...))
818  {
819  rlbox_detail_static_fail_because(
820  cond2,
821  "Change all arguments to the callback have to be tainted or "
822  "tainted_opaque."
823  "For instance if a callback has type\n\n"
824  "int foo(int a, int b) {...}\n\n"
825  "Change this to \n\n"
826  "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox,"
827  "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
828  }
829  else if_constexpr_named(
830  cond3, (std::is_array_v<detail::rlbox_remove_wrapper_t<T_Args>> || ...))
831  {
832  rlbox_detail_static_fail_because(
833  cond3,
834  "Change all static array arguments to the callback to be pointers."
835  "For instance if a callback has type\n\n"
836  "int foo(int a[4]) {...}\n\n"
837  "Change this to \n\n"
838  "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox,"
839  "tainted<int*, T_Sbx> a) {...}\n");
840  }
841  else if_constexpr_named(
842  cond4,
843  !(std::is_void_v<T_Ret> || detail::rlbox_is_tainted_or_opaque_v<T_Ret>))
844  {
845  rlbox_detail_static_fail_because(
846  cond4,
847  "Change the callback return type to be tainted or tainted_opaque if it "
848  "is not void."
849  "For instance if a callback has type\n\n"
850  "int foo(int a, int b) {...}\n\n"
851  "Change this to \n\n"
852  "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox,"
853  "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
854  }
855  else
856  {
857  detail::dynamic_check(
858  sandbox_created.load() == Sandbox_Status::CREATED,
859  "register_callback called without sandbox creation");
860 
861  // Need unique key for each callback we register - just use the func addr
862  void* unique_key = reinterpret_cast<void*>(func_ptr);
863 
864  // Make sure that the user hasn't previously registered this function...
865  // If they have, we would returning 2 owning types (sandbox_callback) to
866  // the same callback which would be bad
867  {
868  std::lock_guard<std::mutex> lock(callback_lock);
869  bool exists =
870  std::find(callback_keys.begin(), callback_keys.end(), unique_key) !=
871  callback_keys.end();
872  detail::dynamic_check(
873  !exists, "You have previously already registered this callback.");
874  callback_keys.push_back(unique_key);
875  }
876 
877  auto callback_interceptor =
878  sandbox_callback_interceptor<detail::rlbox_remove_wrapper_t<T_Ret>,
879  detail::rlbox_remove_wrapper_t<T_Args>...>;
880 
881  auto callback_trampoline = this->template impl_register_callback<
882  detail::convert_to_sandbox_equivalent_t<
883  detail::rlbox_remove_wrapper_t<T_Ret>,
884  T_Sbx>,
885  detail::convert_to_sandbox_equivalent_t<
886  detail::rlbox_remove_wrapper_t<T_Args>,
887  T_Sbx>...>(unique_key, reinterpret_cast<void*>(callback_interceptor));
888 
889  auto tainted_func_ptr = reinterpret_cast<
890  detail::rlbox_tainted_opaque_to_tainted_t<T_Ret, T_Sbx> (*)(
891  T_RL, detail::rlbox_tainted_opaque_to_tainted_t<T_Args, T_Sbx>...)>(
892  reinterpret_cast<void*>(func_ptr));
893 
894  auto ret = sandbox_callback<T_Cb_no_wrap<T_Ret, T_Args...>*, T_Sbx>(
895  this,
896  tainted_func_ptr,
897  callback_interceptor,
898  callback_trampoline,
899  unique_key);
900  return ret;
901  }
902  }
903 
904  // this is an internal function invoked from macros, so it has be public
905  template<typename T>
906  inline tainted<T*, T_Sbx> INTERNAL_get_sandbox_function_name(
907  const char* func_name)
908  {
909  return INTERNAL_get_sandbox_function_ptr<T>(lookup_symbol(func_name));
910  }
911 
912  // this is an internal function invoked from macros, so it has be public
913  template<typename T>
914  inline tainted<T*, T_Sbx> INTERNAL_get_sandbox_function_ptr(void* func_ptr)
915  {
916  return tainted<T*, T_Sbx>::internal_factory(reinterpret_cast<T*>(func_ptr));
917  }
918 
927  template<typename T>
929  {
930  auto idx = app_ptr_map.get_app_pointer_idx((void*)ptr);
931  auto idx_as_ptr = this->template impl_get_unsandboxed_pointer<T>(idx);
932  // Right now we simply assume that any integer can be converted to a valid
933  // pointer in the sandbox This may not be true for some sandboxing mechanism
934  // plugins in the future In this case, we will have to come up with
935  // something more clever to construct indexes that look like valid pointers
936  // Add a check for now to make sure things work fine
937  detail::dynamic_check(is_pointer_in_sandbox_memory(idx_as_ptr),
938  "App pointers are not currently supported for this "
939  "rlbox sandbox plugin. Please file a bug.");
940  auto ret = app_pointer<T*, T_Sbx>(
941  &app_ptr_map, idx, reinterpret_cast<T*>(idx_as_ptr));
942  return ret;
943  }
944 
953  template<typename T>
955  {
956  auto idx = tainted_ptr.get_raw_sandbox_value(*this);
957  void* ret = app_ptr_map.lookup_index(idx);
958  return reinterpret_cast<T*>(ret);
959  }
960 
961 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
962  inline std::vector<rlbox_transition_timing>&
963  process_and_get_transition_times()
964  {
965  return transition_times;
966  }
967  inline int64_t get_total_ns_time_in_sandbox_and_transitions()
968  {
969  int64_t ret = 0;
970  for (auto& transition_time : transition_times) {
971  if (transition_time.invoke == rlbox_transition::INVOKE) {
972  ret += transition_time.time;
973  } else {
974  ret -= transition_time.time;
975  }
976  }
977  return ret;
978  }
979  inline void clear_transition_times() { transition_times.clear(); }
980 #endif
981 };
982 
983 #if defined(__clang__)
984 # pragma clang diagnostic push
985 # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
986 #elif defined(__GNUC__) || defined(__GNUG__)
987 // Can't turn off the variadic macro warning emitted from -pedantic so use a
988 // hack to stop GCC emitting warnings for the reminder of this file
989 # pragma GCC system_header
990 #elif defined(_MSC_VER)
991 // Doesn't seem to emit the warning
992 #else
993 // Don't know the compiler... just let it go through
994 #endif
995 
1004 #ifdef RLBOX_USE_STATIC_CALLS
1005 
1006 # define sandbox_lookup_symbol_helper(prefix, func_name) prefix(func_name)
1007 
1008 # define invoke_sandbox_function(func_name, ...) \
1009  template INTERNAL_invoke_with_func_ptr<decltype(func_name)>( \
1010  #func_name, \
1011  sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name), \
1012  ##__VA_ARGS__)
1013 
1014 # define get_sandbox_function_address(func_name) \
1015  template INTERNAL_get_sandbox_function_ptr<decltype(func_name)>( \
1016  sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name))
1017 
1018 #else
1019 
1020 # define invoke_sandbox_function(func_name, ...) \
1021  template INTERNAL_invoke_with_func_name<decltype(func_name)>( \
1022  #func_name, ##__VA_ARGS__)
1023 
1024 # define get_sandbox_function_address(func_name) \
1025  template INTERNAL_get_sandbox_function_name<decltype(func_name)>(#func_name)
1026 
1027 #endif
1028 
1029 #define sandbox_invoke(sandbox, func_name, ...) \
1030  (sandbox).invoke_sandbox_function(func_name, ##__VA_ARGS__)
1031 
1032 #define sandbox_function_address(sandbox, func_name) \
1033  (sandbox).get_sandbox_function_address(func_name)
1034 
1035 #if defined(__clang__)
1036 # pragma clang diagnostic pop
1037 #else
1038 #endif
1039 
1040 }
Definition: rlbox_policy_types.hpp:182
Encapsulation for sandboxes.
Definition: rlbox_sandbox.hpp:95
T * lookup_app_ptr(tainted< T *, T_Sbx > tainted_ptr)
The mirror of get_app_pointer. Take a tainted pointer which is actually an app_pointer,...
Definition: rlbox_sandbox.hpp:954
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
T * INTERNAL_deny_access(tainted< T *, T_Sbx > src, size_t num, bool &success)
For internal use only. Grant access of the passed in buffer in to the sandbox instance....
Definition: rlbox_sandbox.hpp:651
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
void * sandbox_storage
Unused member that allows the calling code to save data in a "per-sandbox" storage....
Definition: rlbox_sandbox.hpp:339
void free_in_sandbox(tainted_opaque< T, T_Sbx > ptr_opaque)
Free the memory referenced by a tainted_opaque pointer.
Definition: rlbox_sandbox.hpp:574
void free_in_sandbox(tainted< T *, T_Sbx > ptr)
Free the memory referenced by the tainted pointer.
Definition: rlbox_sandbox.hpp:545
auto create_sandbox(T_Args... args)
Create a new sandbox.
Definition: rlbox_sandbox.hpp:361
tainted< T *, T_Sbx > malloc_in_sandbox(uint32_t count)
Allocate an array that is accessible to both the application and sandbox. The pointer is allocated in...
Definition: rlbox_sandbox.hpp:497
tainted< T *, T_Sbx > malloc_in_sandbox()
Allocate a new pointer that is accessible to both the application and sandbox. The pointer is allocat...
Definition: rlbox_sandbox.hpp:478
auto destroy_sandbox()
Destroy sandbox and reclaim any memory.
Definition: rlbox_sandbox.hpp:393
app_pointer< T *, T_Sbx > get_app_pointer(T *ptr)
Create a "fake" pointer referring to a location in the application memory.
Definition: rlbox_sandbox.hpp:928
void free_in_sandbox(tainted_volatile< T, T_Sbx > &ptr_ref)
Free the memory referenced by a tainted_volatile pointer ref.
Definition: rlbox_sandbox.hpp:562
tainted< T *, T_Sbx > INTERNAL_grant_access(T *src, size_t num, bool &success)
For internal use only. Grant access of the passed in buffer in to the sandbox instance....
Definition: rlbox_sandbox.hpp:633
sandbox_callback< T_Cb_no_wrap< T_Ret, T_Args... > *, T_Sbx > register_callback(T_Ret(*func_ptr)(T_RL, T_Args...))
Expose a callback function to the sandboxed code.
Definition: rlbox_sandbox.hpp:799
bool is_pointer_in_app_memory(const void *p)
Check if the pointer points to application memory. For the null-sandbox, this always returns true.
Definition: rlbox_sandbox.hpp:607
Definition: rlbox_policy_types.hpp:37
Definition: rlbox_types.hpp:9
Tainted volatile values are like tainted values but still point to sandbox memory....
Definition: rlbox.hpp:1101
Definition: rlbox_types.hpp:22