RLBox
rlbox_conversion.hpp
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>
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>
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>
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>
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>
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>);
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 }
Definition: rlbox_conversion.hpp:242
Encapsulation for sandboxes.
Definition: rlbox_sandbox.hpp:95