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 <map>
12 #include <mutex>
13 #ifndef RLBOX_USE_CUSTOM_SHARED_LOCK
14 #  include <shared_mutex>
15 #endif
16 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
17 #  include <sstream>
18 #  include <string>
19 #endif
20 #include <type_traits>
21 #include <utility>
22 #include <vector>
23 
24 #include "rlbox_conversion.hpp"
25 #include "rlbox_helpers.hpp"
26 #include "rlbox_stdlib_polyfill.hpp"
27 #include "rlbox_struct_support.hpp"
28 #include "rlbox_type_traits.hpp"
29 #include "rlbox_wrapper_traits.hpp"
30 
31 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
32 using namespace std::chrono;
33 #endif
34 
35 namespace rlbox {
36 
37 namespace convert_fn_ptr_to_sandbox_equivalent_detail {
38   template<typename T, typename T_Sbx>
39   using conv = ::rlbox::detail::convert_to_sandbox_equivalent_t<T, T_Sbx>;
40 
41   template<typename T_Ret, typename... T_Args>
42   using T_Func = T_Ret (*)(T_Args...);
43 
44   template<typename T_Sbx, typename T_Ret, typename... T_Args>
45   T_Func<conv<T_Ret, T_Sbx>, conv<T_Args, T_Sbx>...> helper(
46     T_Ret (*)(T_Args...));
47 }
48 
49 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
50 enum class rlbox_transition
51 {
52   INVOKE,
53   CALLBACK
54 };
55 struct rlbox_transition_timing
56 {
57   rlbox_transition invoke;
58   const char* name;
59   void* ptr;
60   int64_t time;
61 
to_stringrlbox::rlbox_transition_timing62   std::string to_string()
63   {
64     std::ostringstream ret;
65     if (invoke == rlbox_transition::INVOKE) {
66       ret << name;
67     } else {
68       ret << "Callback " << ptr;
69     }
70     ret << " : " << time << "\n";
71 
72     return ret.str();
73   }
74 };
75 #endif
76 
77 #ifndef RLBOX_SINGLE_THREADED_INVOCATIONS
78 #  error                                                                       \
79     "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."
80 #endif
81 
82 /**
83  * @brief Encapsulation for sandboxes.
84  *
85  * @tparam T_Sbx Type of sandbox. For the null sandbox this is
86  * `rlbox_noop_sandbox`
87  */
88 template<typename T_Sbx>
89 class rlbox_sandbox : protected T_Sbx
90 {
91   KEEP_CLASSES_FRIENDLY
92 
93 private:
94 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
95   std::vector<rlbox_transition_timing> transition_times;
96 #endif
97 
98   static inline RLBOX_SHARED_LOCK(sandbox_list_lock);
99   // The actual type of the vector is std::vector<rlbox_sandbox<T_Sbx>*>
100   // However clang 5, 6 have bugs where compilation seg-faults on this type
101   // So we just use this std::vector<void*>
102   static inline std::vector<void*> sandbox_list;
103 
104   RLBOX_SHARED_LOCK(func_ptr_cache_lock);
105   std::map<std::string, void*> func_ptr_map;
106 
107   // This variable tracks of the sandbox has already been created/destroyed.
108   // APIs in this class should be called only when the sandbox is created.
109   // However, it is expensive to check in APIs such as invoke or in the callback
110   // interceptor. What's more, there could be time of check time of use issues
111   // in the checks as well.
112   // In general, we leave it up to the user to ensure these APIs are never
113   // called prior to sandbox construction or after destruction. We perform some
114   // conservative sanity checks, where they would not add too much overhead.
115   enum class Sandbox_Status
116   {
117     NOT_CREATED,
118     INITIALIZING,
119     CREATED,
120     CLEANING_UP
121   };
122   std::atomic<Sandbox_Status> sandbox_created = Sandbox_Status::NOT_CREATED;
123 
124   std::mutex callback_lock;
125   std::vector<void*> callback_keys;
126 
127   template<typename T>
128   using convert_fn_ptr_to_sandbox_equivalent_t = decltype(
129     ::rlbox::convert_fn_ptr_to_sandbox_equivalent_detail::helper<T_Sbx>(
130       std::declval<T>()));
131 
132   template<typename T>
check_invoke_param_type_is_ok()133   inline constexpr void check_invoke_param_type_is_ok()
134   {
135     using T_NoRef = std::remove_reference_t<T>;
136 
137     if_constexpr_named(cond1, detail::rlbox_is_wrapper_v<T_NoRef>)
138     {
139       if_constexpr_named(
140         subcond1,
141         !std::is_same_v<T_Sbx, detail::rlbox_get_wrapper_sandbox_t<T_NoRef>>)
142       {
143         rlbox_detail_static_fail_because(
144           cond1 && subcond1,
145           "Mixing tainted data from a different sandbox types. This could "
146           "happen due to couple of different reasons.\n"
147           "1. You are using 2 sandbox types for example'rlbox_noop_sandbox' "
148           "and 'rlbox_lucet_sandbox', and are passing tainted data from one "
149           "sandbox as parameters into a function call to the other sandbox. "
150           "This is not allowed, unwrap the tainted data with copy_and_verify "
151           "or other unwrapping APIs first.\n"
152           "2. You have inadvertantly forgotten to set/remove "
153           "RLBOX_USE_STATIC_CALLS depending on the sandbox type. Some sandbox "
154           "types like rlbox_noop_sandbox require this to be set to a given "
155           "value, while other types like rlbox_lucet_sandbox, require this not "
156           "to be set.");
157       }
158     }
159     else if_constexpr_named(cond2,
160                             std::is_null_pointer_v<T_NoRef> ||
161                               detail::is_fundamental_or_enum_v<T_NoRef>)
162     {}
163     else
164     {
165       constexpr auto unknownCase = !(cond1 || cond2);
166       rlbox_detail_static_fail_because(
167         unknownCase,
168         "Arguments to a sandbox function call should be primitives  or wrapped "
169         "types like tainted, callbacks etc.");
170     }
171   }
172 
173   template<typename T>
invoke_process_param(T && param)174   inline auto invoke_process_param(T&& param)
175   {
176     check_invoke_param_type_is_ok<T>();
177 
178     using T_NoRef = std::remove_reference_t<T>;
179 
180     if constexpr (detail::rlbox_is_tainted_opaque_v<T_NoRef>) {
181       auto ret = from_opaque(param);
182       return ret.UNSAFE_sandboxed(*this);
183     } else if constexpr (detail::rlbox_is_wrapper_v<T_NoRef>) {
184       return param.UNSAFE_sandboxed(*this);
185     } else if constexpr (std::is_null_pointer_v<T_NoRef>) {
186       tainted<void*, T_Sbx> ret = nullptr;
187       return ret.UNSAFE_sandboxed(*this);
188     } else if constexpr (detail::is_fundamental_or_enum_v<T_NoRef>) {
189       // For unwrapped primitives, assign to a tainted var and then unwrap so
190       // that we adjust for machine model
191       tainted<T_NoRef, T_Sbx> ret = param;
192       return ret.UNSAFE_sandboxed(*this);
193     } else {
194       rlbox_detail_static_fail_because(detail::true_v<T_NoRef>, "Unknown case");
195     }
196   }
197 
198   template<typename T, typename T_Arg>
sandbox_callback_intercept_convert_param(rlbox_sandbox<T_Sbx> & sandbox,const T_Arg & arg)199   inline tainted<T, T_Sbx> sandbox_callback_intercept_convert_param(
200     rlbox_sandbox<T_Sbx>& sandbox,
201     const T_Arg& arg)
202   {
203     tainted<T, T_Sbx> ret;
204     using namespace detail;
205     convert_type<T_Sbx,
206                  adjust_type_direction::TO_APPLICATION,
207                  adjust_type_context::SANDBOX>(
208       ret.get_raw_value_ref(),
209       arg,
210       nullptr /* example_unsandboxed_ptr */,
211       &sandbox);
212     return ret;
213   }
214 
215   template<typename T_Ret, typename... T_Args>
216   static detail::convert_to_sandbox_equivalent_t<T_Ret, T_Sbx>
sandbox_callback_interceptor(detail::convert_to_sandbox_equivalent_t<T_Args,T_Sbx>...args)217   sandbox_callback_interceptor(
218     detail::convert_to_sandbox_equivalent_t<T_Args, T_Sbx>... args)
219   {
220     std::pair<T_Sbx*, void*> context =
221       T_Sbx::impl_get_executed_callback_sandbox_and_key();
222     auto& sandbox = *(reinterpret_cast<rlbox_sandbox<T_Sbx>*>(context.first));
223     auto key = context.second;
224 
225     using T_Func_Ret =
226       std::conditional_t<std::is_void_v<T_Ret>, void, tainted<T_Ret, T_Sbx>>;
227     using T_Func =
228       T_Func_Ret (*)(rlbox_sandbox<T_Sbx>&, tainted<T_Args, T_Sbx>...);
229     auto target_fn_ptr = reinterpret_cast<T_Func>(key);
230 
231 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
232     high_resolution_clock::time_point enter_time = high_resolution_clock::now();
233     auto on_exit = rlbox::detail::make_scope_exit([&] {
234       auto exit_time = high_resolution_clock::now();
235       int64_t ns = duration_cast<nanoseconds>(exit_time - enter_time).count();
236       sandbox.transition_times.push_back(
237         rlbox_transition_timing{ rlbox_transition::CALLBACK,
238                                  nullptr /* func_name */,
239                                  key /* func_ptr */,
240                                  ns });
241     });
242 #endif
243 
244     if constexpr (std::is_void_v<T_Func_Ret>) {
245       (*target_fn_ptr)(
246         sandbox,
247         sandbox.template sandbox_callback_intercept_convert_param<T_Args>(
248           sandbox, args)...);
249       return;
250     } else {
251       auto tainted_ret = (*target_fn_ptr)(
252         sandbox,
253         sandbox.template sandbox_callback_intercept_convert_param<T_Args>(
254           sandbox, args)...);
255 
256       using namespace detail;
257       convert_to_sandbox_equivalent_t<T_Ret, T_Sbx> ret;
258       convert_type<T_Sbx,
259                    adjust_type_direction::TO_SANDBOX,
260                    adjust_type_context::SANDBOX>(
261         ret,
262         tainted_ret.get_raw_value_ref(),
263         nullptr /* example_unsandboxed_ptr */,
264         &sandbox);
265       return ret;
266     }
267   }
268 
269   /**
270    * @brief Unregister a callback function and disallow the sandbox from
271    * calling this function henceforth.
272    */
273   template<typename T_Ret, typename... T_Args>
unregister_callback(void * key)274   inline void unregister_callback(void* key)
275   {
276     // Silently swallowing the failure is better here as RAII types may try to
277     // cleanup callbacks after sandbox destruction
278     if (sandbox_created.load() != Sandbox_Status::CREATED) {
279       return;
280     }
281 
282     this->template impl_unregister_callback<
283       detail::convert_to_sandbox_equivalent_t<T_Ret, T_Sbx>,
284       detail::convert_to_sandbox_equivalent_t<T_Args, T_Sbx>...>(key);
285 
286     std::lock_guard<std::mutex> lock(callback_lock);
287     auto el_ref = std::find(callback_keys.begin(), callback_keys.end(), key);
288     detail::dynamic_check(
289       el_ref != callback_keys.end(),
290       "Unexpected state. Unregistering a callback that was never registered.");
291     callback_keys.erase(el_ref);
292   }
293 
find_sandbox_from_example(const void * example_sandbox_ptr)294   static T_Sbx* find_sandbox_from_example(const void* example_sandbox_ptr)
295   {
296     detail::dynamic_check(
297       example_sandbox_ptr != nullptr,
298       "Internal error: received a null example pointer. Please file a bug.");
299 
300     RLBOX_ACQUIRE_SHARED_GUARD(lock, sandbox_list_lock);
301     for (auto sandbox_v : sandbox_list) {
302       auto sandbox = reinterpret_cast<rlbox_sandbox<T_Sbx>*>(sandbox_v);
303       if (sandbox->is_pointer_in_sandbox_memory(example_sandbox_ptr)) {
304         return sandbox;
305       }
306     }
307 
308     detail::dynamic_check(
309       false,
310       "Internal error: Could not find the sandbox associated with example "
311       "pointer. Please file a bug.");
312     return nullptr;
313   }
314 
315 public:
316   /***** Function to adjust for custom machine models *****/
317 
318   template<typename T>
319   using convert_to_sandbox_equivalent_nonclass_t =
320     detail::convert_base_types_t<T,
321                                  typename T_Sbx::T_ShortType,
322                                  typename T_Sbx::T_IntType,
323                                  typename T_Sbx::T_LongType,
324                                  typename T_Sbx::T_LongLongType,
325                                  typename T_Sbx::T_PointerType>;
326 
get_sandbox_impl()327   T_Sbx* get_sandbox_impl() { return this; }
328 
329   /**
330    * @brief Create a new sandbox.
331    *
332    * @tparam T_args Arguments passed to the underlying sandbox
333    * implementation. For the null sandbox, no arguments are necessary.
334    */
335   template<typename... T_Args>
create_sandbox(T_Args...args)336   inline auto create_sandbox(T_Args... args)
337   {
338 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
339     // Warm up the timer. The first call is always slow (at least on the test
340     // platform)
341     for (int i = 0; i < 10; i++) {
342       auto val = high_resolution_clock::now();
343       RLBOX_UNUSED(val);
344     }
345 #endif
346     auto expected = Sandbox_Status::NOT_CREATED;
347     bool success = sandbox_created.compare_exchange_strong(
348       expected, Sandbox_Status::INITIALIZING /* desired */);
349     detail::dynamic_check(
350       success,
351       "create_sandbox called when sandbox already created/is being "
352       "created concurrently");
353 
354     return detail::return_first_result(
355       [&]() {
356         return this->impl_create_sandbox(std::forward<T_Args>(args)...);
357       },
358       [&]() {
359         sandbox_created.store(Sandbox_Status::CREATED);
360         RLBOX_ACQUIRE_UNIQUE_GUARD(lock, sandbox_list_lock);
361         sandbox_list.push_back(this);
362       });
363   }
364 
365   /**
366    * @brief Destroy sandbox and reclaim any memory.
367    */
destroy_sandbox()368   inline auto destroy_sandbox()
369   {
370     auto expected = Sandbox_Status::CREATED;
371     bool success = sandbox_created.compare_exchange_strong(
372       expected, Sandbox_Status::CLEANING_UP /* desired */);
373 
374     detail::dynamic_check(
375       success,
376       "destroy_sandbox called without sandbox creation/is being "
377       "destroyed concurrently");
378 
379     {
380       RLBOX_ACQUIRE_UNIQUE_GUARD(lock, sandbox_list_lock);
381       auto el_ref = std::find(sandbox_list.begin(), sandbox_list.end(), this);
382       detail::dynamic_check(
383         el_ref != sandbox_list.end(),
384         "Unexpected state. Destroying a sandbox that was never initialized.");
385       sandbox_list.erase(el_ref);
386     }
387 
388     sandbox_created.store(Sandbox_Status::NOT_CREATED);
389     return this->impl_destroy_sandbox();
390   }
391 
392   template<typename T>
get_unsandboxed_pointer(convert_to_sandbox_equivalent_nonclass_t<T> p) const393   inline T get_unsandboxed_pointer(
394     convert_to_sandbox_equivalent_nonclass_t<T> p) const
395   {
396     static_assert(std::is_pointer_v<T>);
397     if (p == 0) {
398       return nullptr;
399     }
400     auto ret = this->template impl_get_unsandboxed_pointer<T>(p);
401     return reinterpret_cast<T>(ret);
402   }
403 
404   template<typename T>
get_sandboxed_pointer(const void * p) const405   inline convert_to_sandbox_equivalent_nonclass_t<T> get_sandboxed_pointer(
406     const void* p) const
407   {
408     static_assert(std::is_pointer_v<T>);
409     if (p == nullptr) {
410       return 0;
411     }
412     return this->template impl_get_sandboxed_pointer<T>(p);
413   }
414 
415   template<typename T>
get_unsandboxed_pointer_no_ctx(convert_to_sandbox_equivalent_nonclass_t<T> p,const void * example_unsandboxed_ptr)416   static inline T get_unsandboxed_pointer_no_ctx(
417     convert_to_sandbox_equivalent_nonclass_t<T> p,
418     const void* example_unsandboxed_ptr)
419   {
420     static_assert(std::is_pointer_v<T>);
421     if (p == 0) {
422       return nullptr;
423     }
424     auto ret = T_Sbx::template impl_get_unsandboxed_pointer_no_ctx<T>(
425       p, example_unsandboxed_ptr, find_sandbox_from_example);
426     return reinterpret_cast<T>(ret);
427   }
428 
429   template<typename T>
430   static inline convert_to_sandbox_equivalent_nonclass_t<T>
get_sandboxed_pointer_no_ctx(const void * p,const void * example_unsandboxed_ptr)431   get_sandboxed_pointer_no_ctx(const void* p,
432                                const void* example_unsandboxed_ptr)
433   {
434     static_assert(std::is_pointer_v<T>);
435     if (p == nullptr) {
436       return 0;
437     }
438     return T_Sbx::template impl_get_sandboxed_pointer_no_ctx<T>(
439       p, example_unsandboxed_ptr, find_sandbox_from_example);
440   }
441 
442   /**
443    * @brief Allocate a new pointer that is accessible to both the application
444    * and sandbox. The pointer is allocated in sandbox memory.
445    *
446    * @tparam T The type of the pointer you want to create. If T=int, this
447    * would return a pointer to an int.
448    *
449    * @return tainted<T*, T_Sbx> Tainted pointer accessible to the application
450    * and sandbox.
451    */
452   template<typename T>
malloc_in_sandbox()453   inline tainted<T*, T_Sbx> malloc_in_sandbox()
454   {
455     const uint32_t defaultCount = 1;
456     return malloc_in_sandbox<T>(defaultCount);
457   }
458 
459   /**
460    * @brief Allocate an array that is accessible to both the application
461    * and sandbox. The pointer is allocated in sandbox memory.
462    *
463    * @tparam T The type of the array elements you want to create. If T=int, this
464    * would return a pointer to an array of ints.
465    *
466    * @param count The number of array elements to allocate.
467    *
468    * @return tainted<T*, T_Sbx> Tainted pointer accessible to the application
469    * and sandbox.
470    */
471   template<typename T>
malloc_in_sandbox(uint32_t count)472   inline tainted<T*, T_Sbx> malloc_in_sandbox(uint32_t count)
473   {
474     // Silently swallowing the failure is better here as RAII types may try to
475     // malloc after sandbox destruction
476     if (sandbox_created.load() != Sandbox_Status::CREATED) {
477       return tainted<T*, T_Sbx>::internal_factory(nullptr);
478     }
479 
480     detail::dynamic_check(count != 0, "Malloc tried to allocate 0 bytes");
481     auto ptr_in_sandbox = this->impl_malloc_in_sandbox(sizeof(T) * count);
482     auto ptr = get_unsandboxed_pointer<T*>(ptr_in_sandbox);
483     if (!ptr) {
484       return tainted<T*, T_Sbx>(nullptr);
485     }
486     detail::dynamic_check(is_pointer_in_sandbox_memory(ptr),
487                           "Malloc returned pointer outside the sandbox memory");
488     auto ptr_end = reinterpret_cast<uintptr_t>(ptr + (count - 1));
489     detail::dynamic_check(
490       is_in_same_sandbox(ptr, reinterpret_cast<void*>(ptr_end)),
491       "Malloc returned a pointer whose range goes beyond sandbox memory");
492     auto cast_ptr = reinterpret_cast<T*>(ptr);
493     return tainted<T*, T_Sbx>::internal_factory(cast_ptr);
494   }
495 
496   /**
497    * @brief Free the memory referenced by the tainted pointer.
498    *
499    * @param ptr Pointer to sandbox memory to free.
500    */
501   template<typename T>
free_in_sandbox(tainted<T *,T_Sbx> ptr)502   inline void free_in_sandbox(tainted<T*, T_Sbx> ptr)
503   {
504     // Silently swallowing the failure is better here as RAII types may try to
505     // free after sandbox destruction
506     if (sandbox_created.load() != Sandbox_Status::CREATED) {
507       return;
508     }
509 
510     this->impl_free_in_sandbox(ptr.get_raw_sandbox_value(*this));
511   }
512 
513   /**
514    * @brief Check if two pointers are in the same sandbox.
515    * For the null-sandbox, this always returns true.
516    */
is_in_same_sandbox(const void * p1,const void * p2)517   static inline bool is_in_same_sandbox(const void* p1, const void* p2)
518   {
519     return T_Sbx::impl_is_in_same_sandbox(p1, p2);
520   }
521 
522   /**
523    * @brief Check if the pointer points to this sandbox's memory.
524    * For the null-sandbox, this always returns true.
525    */
is_pointer_in_sandbox_memory(const void * p)526   inline bool is_pointer_in_sandbox_memory(const void* p)
527   {
528     return this->impl_is_pointer_in_sandbox_memory(p);
529   }
530 
531   /**
532    * @brief Check if the pointer points to application memory.
533    * For the null-sandbox, this always returns true.
534    */
is_pointer_in_app_memory(const void * p)535   inline bool is_pointer_in_app_memory(const void* p)
536   {
537     return this->impl_is_pointer_in_app_memory(p);
538   }
539 
get_total_memory()540   inline size_t get_total_memory() { return this->impl_get_total_memory(); }
541 
get_memory_location()542   inline void* get_memory_location()
543   {
544     return this->impl_get_memory_location();
545   }
546 
lookup_symbol(const char * func_name)547   void* lookup_symbol(const char* func_name)
548   {
549     {
550       RLBOX_ACQUIRE_SHARED_GUARD(lock, func_ptr_cache_lock);
551 
552       auto func_ptr_ref = func_ptr_map.find(func_name);
553       if (func_ptr_ref != func_ptr_map.end()) {
554         return func_ptr_ref->second;
555       }
556     }
557 
558     void* func_ptr = this->impl_lookup_symbol(func_name);
559     RLBOX_ACQUIRE_UNIQUE_GUARD(lock, func_ptr_cache_lock);
560     func_ptr_map[func_name] = func_ptr;
561     return func_ptr;
562   }
563 
564   // this is an internal function invoked from macros, so it has be public
565   template<typename T, typename... T_Args>
INTERNAL_invoke_with_func_name(const char * func_name,T_Args &&...params)566   inline auto INTERNAL_invoke_with_func_name(const char* func_name,
567                                              T_Args&&... params)
568   {
569     return INTERNAL_invoke_with_func_ptr<T, T_Args...>(
570       func_name, lookup_symbol(func_name), std::forward<T_Args>(params)...);
571   }
572 
573   // this is an internal function invoked from macros, so it has be public
574   // Explicitly don't use inline on this, as this adds a lot of instructions
575   // prior to function call. What's more, by not inlining, different function
576   // calls with the same signature can share the same code segments for
577   // sandboxed function execution in the binary
578   template<typename T, typename... T_Args>
INTERNAL_invoke_with_func_ptr(const char * func_name,void * func_ptr,T_Args &&...params)579   auto INTERNAL_invoke_with_func_ptr(const char* func_name,
580                                      void* func_ptr,
581                                      T_Args&&... params)
582   {
583     // unused in some paths
584     RLBOX_UNUSED(func_name);
585 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
586     auto enter_time = high_resolution_clock::now();
587     auto on_exit = rlbox::detail::make_scope_exit([&] {
588       auto exit_time = high_resolution_clock::now();
589       int64_t ns = duration_cast<nanoseconds>(exit_time - enter_time).count();
590       transition_times.push_back(rlbox_transition_timing{
591         rlbox_transition::INVOKE, func_name, func_ptr, ns });
592     });
593 #endif
594     (check_invoke_param_type_is_ok<T_Args>(), ...);
595 
596     static_assert(
597       rlbox::detail::polyfill::is_invocable_v<
598         T,
599         detail::rlbox_remove_wrapper_t<std::remove_reference_t<T_Args>>...>,
600       "Mismatched arguments types for function");
601 
602     using T_Result = rlbox::detail::polyfill::invoke_result_t<
603       T,
604       detail::rlbox_remove_wrapper_t<std::remove_reference_t<T_Args>>...>;
605 
606     using T_Converted =
607       std::remove_pointer_t<convert_fn_ptr_to_sandbox_equivalent_t<T*>>;
608 
609     if constexpr (std::is_void_v<T_Result>) {
610       this->template impl_invoke_with_func_ptr<T>(
611         reinterpret_cast<T_Converted*>(func_ptr),
612         invoke_process_param(params)...);
613       return;
614     } else {
615       auto raw_result = this->template impl_invoke_with_func_ptr<T>(
616         reinterpret_cast<T_Converted*>(func_ptr),
617         invoke_process_param(params)...);
618       tainted<T_Result, T_Sbx> wrapped_result;
619       using namespace detail;
620       convert_type<T_Sbx,
621                    adjust_type_direction::TO_APPLICATION,
622                    adjust_type_context::SANDBOX>(
623         wrapped_result.get_raw_value_ref(),
624         raw_result,
625         nullptr /* example_unsandboxed_ptr */,
626         this /* sandbox_ptr */);
627       return wrapped_result;
628     }
629   }
630 
631   // Useful in the porting stage to temporarily allow non tainted pointers to go
632   // through. This will only ever work in the rlbox_noop_sandbox. Any sandbox
633   // that actually enforces isolation will crash here.
634   template<typename T2>
UNSAFE_accept_pointer(T2 ptr)635   tainted<T2, T_Sbx> UNSAFE_accept_pointer(T2 ptr)
636   {
637     static_assert(std::is_pointer_v<T2>,
638                   "UNSAFE_accept_pointer expects a pointer param");
639     tainted<T2, T_Sbx> ret;
640     ret.assign_raw_pointer(*this, ptr);
641     return ret;
642   }
643 
644   template<typename T_Ret, typename... T_Args>
645   using T_Cb_no_wrap = detail::rlbox_remove_wrapper_t<T_Ret>(
646     detail::rlbox_remove_wrapper_t<T_Args>...);
647 
648   template<typename T_Ret>
register_callback(T_Ret (*)())649   sandbox_callback<T_Cb_no_wrap<T_Ret>*, T_Sbx> register_callback(T_Ret (*)())
650   {
651     rlbox_detail_static_fail_because(
652       detail::true_v<T_Ret>,
653       "Modify the callback to change the first parameter to a sandbox."
654       "For instance if a callback has type\n\n"
655       "int foo() {...}\n\n"
656       "Change this to \n\n"
657       "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox) {...}\n");
658 
659     // this is never executed, but we need it for the function to type-check
660     std::abort();
661   }
662 
663   /**
664    * @brief Expose a callback function to the sandboxed code.
665    *
666    * @param func_ptr The callback to expose.
667    *
668    * @tparam T_RL   Sandbox reference type (first argument).
669    * @tparam T_Ret  Return type of callback. Must be tainted or void.
670    * @tparam T_Args Types of remaining callback arguments. Must be tainted.
671    *
672    * @return Wrapped callback function pointer that can be passed to the
673    * sandbox.
674    */
675   template<typename T_RL, typename T_Ret, typename... T_Args>
register_callback(T_Ret (* func_ptr)(T_RL,T_Args...))676   sandbox_callback<T_Cb_no_wrap<T_Ret, T_Args...>*, T_Sbx> register_callback(
677     T_Ret (*func_ptr)(T_RL, T_Args...))
678   {
679     // Some branches don't use the param
680     RLBOX_UNUSED(func_ptr);
681 
682     if_constexpr_named(cond1, !std::is_same_v<T_RL, rlbox_sandbox<T_Sbx>&>)
683     {
684       rlbox_detail_static_fail_because(
685         cond1,
686         "Modify the callback to change the first parameter to a sandbox."
687         "For instance if a callback has type\n\n"
688         "int foo(int a, int b) {...}\n\n"
689         "Change this to \n\n"
690         "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox,"
691         "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
692     }
693     else if_constexpr_named(
694       cond2, !(detail::rlbox_is_tainted_or_opaque_v<T_Args> && ...))
695     {
696       rlbox_detail_static_fail_because(
697         cond2,
698         "Change all arguments to the callback have to be tainted or "
699         "tainted_opaque."
700         "For instance if a callback has type\n\n"
701         "int foo(int a, int b) {...}\n\n"
702         "Change this to \n\n"
703         "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox,"
704         "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
705     }
706     else if_constexpr_named(
707       cond3, (std::is_array_v<detail::rlbox_remove_wrapper_t<T_Args>> || ...))
708     {
709       rlbox_detail_static_fail_because(
710         cond3,
711         "Change all static array arguments to the callback to be pointers."
712         "For instance if a callback has type\n\n"
713         "int foo(int a[4]) {...}\n\n"
714         "Change this to \n\n"
715         "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox,"
716         "tainted<int*, T_Sbx> a) {...}\n");
717     }
718     else if_constexpr_named(
719       cond4,
720       !(std::is_void_v<T_Ret> || detail::rlbox_is_tainted_or_opaque_v<T_Ret>))
721     {
722       rlbox_detail_static_fail_because(
723         cond4,
724         "Change the callback return type to be tainted or tainted_opaque if it "
725         "is not void."
726         "For instance if a callback has type\n\n"
727         "int foo(int a, int b) {...}\n\n"
728         "Change this to \n\n"
729         "tainted<int, T_Sbx> foo(rlbox_sandbox<T_Sbx>& sandbox,"
730         "tainted<int, T_Sbx> a, tainted<int, T_Sbx> b) {...}\n");
731     }
732     else
733     {
734       detail::dynamic_check(
735         sandbox_created.load() == Sandbox_Status::CREATED,
736         "register_callback called without sandbox creation");
737 
738       // Need unique key for each callback we register - just use the func addr
739       void* unique_key = reinterpret_cast<void*>(func_ptr);
740 
741       // Make sure that the user hasn't previously registered this function...
742       // If they have, we would returning 2 owning types (sandbox_callback) to
743       // the same callback which would be bad
744       {
745         std::lock_guard<std::mutex> lock(callback_lock);
746         bool exists =
747           std::find(callback_keys.begin(), callback_keys.end(), unique_key) !=
748           callback_keys.end();
749         detail::dynamic_check(
750           !exists, "You have previously already registered this callback.");
751         callback_keys.push_back(unique_key);
752       }
753 
754       auto callback_interceptor =
755         sandbox_callback_interceptor<detail::rlbox_remove_wrapper_t<T_Ret>,
756                                      detail::rlbox_remove_wrapper_t<T_Args>...>;
757 
758       auto callback_trampoline = this->template impl_register_callback<
759         detail::convert_to_sandbox_equivalent_t<
760           detail::rlbox_remove_wrapper_t<T_Ret>,
761           T_Sbx>,
762         detail::convert_to_sandbox_equivalent_t<
763           detail::rlbox_remove_wrapper_t<T_Args>,
764           T_Sbx>...>(unique_key, reinterpret_cast<void*>(callback_interceptor));
765 
766       auto tainted_func_ptr = reinterpret_cast<
767         detail::rlbox_tainted_opaque_to_tainted_t<T_Ret, T_Sbx> (*)(
768           T_RL, detail::rlbox_tainted_opaque_to_tainted_t<T_Args, T_Sbx>...)>(
769         reinterpret_cast<void*>(func_ptr));
770 
771       auto ret = sandbox_callback<T_Cb_no_wrap<T_Ret, T_Args...>*, T_Sbx>(
772         this,
773         tainted_func_ptr,
774         callback_interceptor,
775         callback_trampoline,
776         unique_key);
777       return ret;
778     }
779   }
780 
781   // this is an internal function invoked from macros, so it has be public
782   template<typename T>
INTERNAL_get_sandbox_function_name(const char * func_name)783   inline tainted<T*, T_Sbx> INTERNAL_get_sandbox_function_name(
784     const char* func_name)
785   {
786     return INTERNAL_get_sandbox_function_ptr<T>(lookup_symbol(func_name));
787   }
788 
789   // this is an internal function invoked from macros, so it has be public
790   template<typename T>
INTERNAL_get_sandbox_function_ptr(void * func_ptr)791   inline tainted<T*, T_Sbx> INTERNAL_get_sandbox_function_ptr(void* func_ptr)
792   {
793     return tainted<T*, T_Sbx>::internal_factory(reinterpret_cast<T*>(func_ptr));
794   }
795 
796 #ifdef RLBOX_MEASURE_TRANSITION_TIMES
797   inline std::vector<rlbox_transition_timing>&
process_and_get_transition_times()798   process_and_get_transition_times()
799   {
800     return transition_times;
801   }
802 #endif
803 };
804 
805 #if defined(__clang__)
806 #  pragma clang diagnostic push
807 #  pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
808 #elif defined(__GNUC__) || defined(__GNUG__)
809 // Can't turn off the variadic macro warning emitted from -pedantic so use a
810 // hack to stop GCC emitting warnings for the reminder of this file
811 #  pragma GCC system_header
812 #elif defined(_MSC_VER)
813 // Doesn't seem to emit the warning
814 #else
815 // Don't know the compiler... just let it go through
816 #endif
817 
818 /**
819  * @def  invoke_sandbox_function
820  * @brief Call sandbox function.
821  *
822  * @param func_name The sandboxed library function to call.
823  * @param ... Arguments to function should be simple or tainted values.
824  * @return Tainted value or void.
825  */
826 #ifdef RLBOX_USE_STATIC_CALLS
827 
828 #  define sandbox_lookup_symbol_helper(prefix, func_name) prefix(func_name)
829 
830 #  define invoke_sandbox_function(func_name, ...)                              \
831     template INTERNAL_invoke_with_func_ptr<decltype(func_name)>(               \
832       #func_name,                                                              \
833       sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name),       \
834       ##__VA_ARGS__)
835 
836 #  define get_sandbox_function_address(func_name)                              \
837     template INTERNAL_get_sandbox_function_ptr<decltype(func_name)>(           \
838       sandbox_lookup_symbol_helper(RLBOX_USE_STATIC_CALLS(), func_name))
839 
840 #else
841 
842 #  define invoke_sandbox_function(func_name, ...)                              \
843     template INTERNAL_invoke_with_func_name<decltype(func_name)>(              \
844       #func_name, ##__VA_ARGS__)
845 
846 #  define get_sandbox_function_address(func_name)                              \
847     template INTERNAL_get_sandbox_function_name<decltype(func_name)>(#func_name)
848 
849 #endif
850 
851 #define sandbox_invoke(sandbox, func_name, ...)                                \
852   (sandbox).invoke_sandbox_function(func_name, ##__VA_ARGS__)
853 
854 #define sandbox_function_address(sandbox, func_name)                           \
855   (sandbox).get_sandbox_function_address(func_name)
856 
857 #if defined(__clang__)
858 #  pragma clang diagnostic pop
859 #else
860 #endif
861 
862 }
863