1 #pragma once
2 // IWYU pragma: private, include "rlbox.hpp"
3 // IWYU pragma: friend "rlbox_.*\.hpp"
4 
5 #include <array>
6 #include <cstring>
7 #include <limits>
8 #include <type_traits>
9 
10 #include "rlbox_helpers.hpp"
11 #include "rlbox_type_traits.hpp"
12 #include "rlbox_types.hpp"
13 
14 namespace rlbox::detail {
15 
16 template<typename T_To, typename T_From>
convert_type_fundamental(T_To & to,const volatile T_From & from)17 inline constexpr void convert_type_fundamental(T_To& to,
18                                                const volatile T_From& from)
19 {
20   using namespace std;
21 
22   if_constexpr_named(cond1, !is_fundamental_or_enum_v<T_To>)
23   {
24     rlbox_detail_static_fail_because(
25       cond1, "Conversion target should be fundamental or enum type");
26   }
27   else if_constexpr_named(cond2, !is_fundamental_or_enum_v<T_From>)
28   {
29     rlbox_detail_static_fail_because(
30       cond2, "Conversion source should be fundamental or enum type");
31   }
32   else if_constexpr_named(cond3, is_enum_v<T_To> || is_enum_v<T_From>)
33   {
34     static_assert(std::is_same_v<detail::remove_cv_ref_t<T_To>,
35                                  detail::remove_cv_ref_t<T_From>>);
36     to = from;
37   }
38   else if_constexpr_named(
39     cond4, is_floating_point_v<T_To> || is_floating_point_v<T_From>)
40   {
41     static_assert(is_floating_point_v<T_To> && is_floating_point_v<T_From>);
42     // language coerces different float types
43     to = from;
44   }
45   else if_constexpr_named(cond5, is_integral_v<T_To> || is_integral_v<T_From>)
46   {
47     static_assert(is_integral_v<T_To> && is_integral_v<T_From>);
48 
49     const char* err_msg =
50       "Over/Underflow when converting between integer types";
51 
52     // Some branches don't use the param
53     RLBOX_UNUSED(err_msg);
54 
55     if constexpr (is_signed_v<T_To> == is_signed_v<T_From> &&
56                   sizeof(T_To) >= sizeof(T_From)) {
57       // Eg: int64_t from int32_t, uint64_t from uint32_t
58     } else if constexpr (is_unsigned_v<T_To> && is_unsigned_v<T_From>) {
59       // Eg: uint32_t from uint64_t
60       dynamic_check(from <= numeric_limits<T_To>::max(), err_msg);
61     } else if constexpr (is_signed_v<T_To> && is_signed_v<T_From>) {
62       // Eg: int32_t from int64_t
63       dynamic_check(from >= numeric_limits<T_To>::min(), err_msg);
64       dynamic_check(from <= numeric_limits<T_To>::max(), err_msg);
65     } else if constexpr (is_unsigned_v<T_To> && is_signed_v<T_From>) {
66       if constexpr (sizeof(T_To) < sizeof(T_From)) {
67         // Eg: uint32_t from int64_t
68         dynamic_check(from >= 0, err_msg);
69         auto to_max = numeric_limits<T_To>::max();
70         dynamic_check(from <= static_cast<T_From>(to_max), err_msg);
71       } else {
72         // Eg: uint32_t from int32_t, uint64_t from int32_t
73         dynamic_check(from >= 0, err_msg);
74       }
75     } else if constexpr (is_signed_v<T_To> && is_unsigned_v<T_From>) {
76       if constexpr (sizeof(T_To) <= sizeof(T_From)) {
77         // Eg: int32_t from uint32_t, int32_t from uint64_t
78         auto to_max = numeric_limits<T_To>::max();
79         dynamic_check(from <= static_cast<T_From>(to_max), err_msg);
80       } else {
81         // Eg: int64_t from uint32_t
82       }
83     }
84     to = static_cast<T_To>(from);
85   }
86   else
87   {
88     constexpr auto unknownCase = !(cond1 || cond2 || cond3 || cond4 || cond5);
89     rlbox_detail_static_fail_because(
90       unknownCase, "Unexpected case for convert_type_fundamental");
91   }
92 }
93 
94 template<typename T_To, typename T_From>
convert_type_fundamental_or_array(T_To & to,const T_From & from)95 inline constexpr void convert_type_fundamental_or_array(T_To& to,
96                                                         const T_From& from)
97 {
98   using namespace std;
99 
100   using T_To_C = std_array_to_c_arr_t<T_To>;
101   using T_From_C = std_array_to_c_arr_t<T_From>;
102   using T_To_El = remove_all_extents_t<T_To_C>;
103   using T_From_El = remove_all_extents_t<T_From_C>;
104 
105   if_constexpr_named(cond1, is_array_v<T_To_C> != is_array_v<T_From_C>)
106   {
107     rlbox_detail_static_fail_because(
108       cond1, "Conversion should not go between array and non array types");
109   }
110   else if constexpr (!is_array_v<T_To_C>)
111   {
112     convert_type_fundamental(to, from);
113   }
114   else if_constexpr_named(cond2, !all_extents_same<T_To_C, T_From_C>)
115   {
116     rlbox_detail_static_fail_because(
117       cond2, "Conversion between arrays should have same dimensions");
118   }
119   else if_constexpr_named(cond3,
120                           is_pointer_v<T_To_El> || is_pointer_v<T_From_El>)
121   {
122     rlbox_detail_static_fail_because(cond3,
123                                      "convert_type_fundamental_or_array "
124                                      "does not allow arrays of pointers");
125   }
126   else
127   {
128     // Explicitly using size to check for element type as we may be going across
129     // different types of the same width such as void* and uintptr_t
130     if constexpr (sizeof(T_To_El) == sizeof(T_From_El) &&
131                   is_signed_v<T_To_El> == is_signed_v<T_From_El>) {
132       // Sanity check - this should definitely be true
133       static_assert(sizeof(T_From_C) == sizeof(T_To_C));
134       std::memcpy(&to, &from, sizeof(T_To_C));
135     } else {
136       for (size_t i = 0; i < std::extent_v<T_To_C>; i++) {
137         convert_type_fundamental_or_array(to[i], from[i]);
138       }
139     }
140   }
141 }
142 
143 enum class adjust_type_direction
144 {
145   TO_SANDBOX,
146   TO_APPLICATION,
147   NO_CHANGE
148 };
149 
150 enum class adjust_type_context
151 {
152   EXAMPLE,
153   SANDBOX
154 };
155 
156 template<typename T_Sbx,
157          adjust_type_direction Direction,
158          adjust_type_context Context,
159          typename T_To,
160          typename T_From>
convert_type_non_class(T_To & to,const T_From & from,const void * example_unsandboxed_ptr,rlbox_sandbox<T_Sbx> * sandbox_ptr)161 inline constexpr void convert_type_non_class(
162   T_To& to,
163   const T_From& from,
164   const void* example_unsandboxed_ptr,
165   rlbox_sandbox<T_Sbx>* sandbox_ptr)
166 {
167   using namespace std;
168 
169   // Some branches don't use the param
170   RLBOX_UNUSED(example_unsandboxed_ptr);
171   RLBOX_UNUSED(sandbox_ptr);
172 
173   using T_To_C = std_array_to_c_arr_t<T_To>;
174   using T_From_C = std_array_to_c_arr_t<T_From>;
175   using T_To_El = remove_all_extents_t<T_To_C>;
176   using T_From_El = remove_all_extents_t<T_From_C>;
177 
178   if constexpr (is_pointer_v<T_To_C> || is_pointer_v<T_From_C>) {
179 
180     if constexpr (Direction == adjust_type_direction::NO_CHANGE) {
181 
182       static_assert(is_pointer_v<T_To_C> && is_pointer_v<T_From_C> &&
183                     sizeof(T_To_C) == sizeof(T_From_C));
184       to = from;
185 
186     } else if constexpr (Direction == adjust_type_direction::TO_SANDBOX) {
187 
188       static_assert(is_pointer_v<T_From_C>);
189       // Maybe a function pointer, so convert
190       auto from_c = reinterpret_cast<const void*>(from);
191       if constexpr (Context == adjust_type_context::SANDBOX) {
192         RLBOX_DEBUG_ASSERT(sandbox_ptr != nullptr);
193         to = sandbox_ptr->template get_sandboxed_pointer<T_From_C>(from_c);
194       } else {
195         RLBOX_DEBUG_ASSERT(from_c == nullptr ||
196                            example_unsandboxed_ptr != nullptr);
197         to =
198           rlbox_sandbox<T_Sbx>::template get_sandboxed_pointer_no_ctx<T_From_C>(
199             from_c, example_unsandboxed_ptr);
200       }
201 
202     } else if constexpr (Direction == adjust_type_direction::TO_APPLICATION) {
203 
204       static_assert(is_pointer_v<T_To_C>);
205       if constexpr (Context == adjust_type_context::SANDBOX) {
206         RLBOX_DEBUG_ASSERT(sandbox_ptr != nullptr);
207         to = sandbox_ptr->template get_unsandboxed_pointer<T_To_C>(from);
208       } else {
209         RLBOX_DEBUG_ASSERT(from == 0 || example_unsandboxed_ptr != nullptr);
210         to =
211           rlbox_sandbox<T_Sbx>::template get_unsandboxed_pointer_no_ctx<T_To_C>(
212             from, example_unsandboxed_ptr);
213       }
214     }
215 
216   } else if constexpr (is_pointer_v<T_To_El> || is_pointer_v<T_From_El>) {
217 
218     if constexpr (Direction == adjust_type_direction::NO_CHANGE) {
219       // Sanity check - this should definitely be true
220       static_assert(sizeof(T_To_El) == sizeof(T_From_El) &&
221                     sizeof(T_From_C) == sizeof(T_To_C));
222       memcpy(&to, &from, sizeof(T_To_C));
223     } else {
224       for (size_t i = 0; i < std::extent_v<T_To_C>; i++) {
225         convert_type_non_class<T_Sbx, Direction, Context>(
226           to[i], from[i], example_unsandboxed_ptr, sandbox_ptr);
227       }
228     }
229 
230   } else {
231     convert_type_fundamental_or_array(to, from);
232   }
233 }
234 
235 // Structs implement their own convert_type by specializing this class
236 // Have to do this via a class, as functions can't be partially specialized
237 template<typename T_Sbx,
238          adjust_type_direction Direction,
239          adjust_type_context Context,
240          typename T_To,
241          typename T_From>
242 class convert_type_class;
243 // The specialization implements the following
244 // {
245 //   static inline void run(T_To& to,
246 //                          const T_From& from,
247 //                          const void* example_unsandboxed_ptr);
248 // }
249 
250 template<typename T_Sbx,
251          adjust_type_direction Direction,
252          adjust_type_context Context,
253          typename T_To,
254          typename T_From>
convert_type(T_To & to,const T_From & from,const void * example_unsandboxed_ptr,rlbox_sandbox<T_Sbx> * sandbox_ptr)255 inline void convert_type(T_To& to,
256                          const T_From& from,
257                          const void* example_unsandboxed_ptr,
258                          rlbox_sandbox<T_Sbx>* sandbox_ptr)
259 {
260   if constexpr ((std::is_class_v<T_To> ||
261                  std::is_class_v<T_From>)&&!detail::is_std_array_v<T_To> &&
262                 !detail::is_std_array_v<T_From>) {
263     // Sanity check
264     static_assert(std::is_class_v<T_From> && std::is_class_v<T_To>);
265     convert_type_class<T_Sbx, Direction, Context, T_To, T_From>::run(
266       to, from, example_unsandboxed_ptr, sandbox_ptr);
267   } else {
268     convert_type_non_class<T_Sbx, Direction, Context>(
269       to, from, example_unsandboxed_ptr, sandbox_ptr);
270   }
271 }
272 
273 }