]>
Commit | Line | Data |
---|---|---|
f427ee49 A |
1 | // |
2 | // Copyright (c) 2019 Apple, Inc. All rights reserved. | |
3 | // | |
4 | // @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | // | |
6 | // This file contains Original Code and/or Modifications of Original Code | |
7 | // as defined in and that are subject to the Apple Public Source License | |
8 | // Version 2.0 (the 'License'). You may not use this file except in | |
9 | // compliance with the License. The rights granted to you under the License | |
10 | // may not be used to create, or enable the creation or redistribution of, | |
11 | // unlawful or unlicensed copies of an Apple operating system, or to | |
12 | // circumvent, violate, or enable the circumvention or violation of, any | |
13 | // terms of an Apple operating system software license agreement. | |
14 | // | |
15 | // Please obtain a copy of the License at | |
16 | // http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | // | |
18 | // The Original Code and all software distributed under the License are | |
19 | // distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | // EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | // INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | // FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | // Please see the License for the specific language governing rights and | |
24 | // limitations under the License. | |
25 | // | |
26 | // @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | // | |
28 | ||
29 | #ifndef XNU_LIBKERN_LIBKERN_CXX_BOUNDED_PTR_H | |
30 | #define XNU_LIBKERN_LIBKERN_CXX_BOUNDED_PTR_H | |
31 | ||
32 | #include <stddef.h> | |
33 | #include <stdint.h> | |
34 | #include <os/overflow.h> | |
35 | #include <os/base.h> | |
36 | ||
37 | #if !defined(__improbable) | |
38 | # define __improbable(...) __builtin_expect((__VA_ARGS__), 0) | |
39 | #endif | |
40 | ||
41 | namespace libkern { | |
42 | namespace detail { | |
43 | // Reimplementation of things in <type_traits> because we don't seem | |
44 | // to have the right to rely on the C++ Standard Library (based on | |
45 | // attempts to compile IOHIDFamily). | |
46 | // TODO: Do we really need to re-implement this here? | |
47 | template <typename ...> using void_t = void; | |
48 | template <typename T> T && declval() noexcept; | |
49 | using nullptr_t = decltype(nullptr); | |
50 | template <bool Cond, typename T = void> struct enable_if; | |
51 | template <typename T> struct enable_if<true, T> { using type = T; }; | |
52 | template <bool Cond, typename T = void> using enable_if_t = typename enable_if<Cond, T>::type; | |
53 | template <typename T1, typename T2> | |
54 | constexpr bool is_convertible_v = __is_convertible_to(T1, T2); | |
55 | ||
56 | template <typename T> inline constexpr bool is_void_v = false; | |
57 | template <> inline constexpr bool is_void_v<void> = true; | |
58 | template <> inline constexpr bool is_void_v<void const> = true; | |
59 | ||
60 | template <typename T, typename U> struct copy_const { using type = U; }; | |
61 | template <typename T, typename U> struct copy_const<T const, U> { using type = U const; }; | |
62 | template <typename T, typename U> using copy_const_t = typename copy_const<T, U>::type; | |
63 | ||
64 | template <typename T, typename U> struct copy_cv { using type = U; }; | |
65 | template <typename T, typename U> struct copy_cv<T const, U> { using type = U const; }; | |
66 | template <typename T, typename U> struct copy_cv<T volatile, U> { using type = U volatile; }; | |
67 | template <typename T, typename U> struct copy_cv<T const volatile, U> { using type = U const volatile; }; | |
68 | template <typename T, typename U> using copy_cv_t = typename copy_cv<T, U>::type; | |
69 | ||
70 | template <typename T, typename U> | |
71 | using WhenComparable = void_t< | |
72 | decltype(declval<T>() == declval<U>()), | |
73 | decltype(declval<T>() != declval<U>()) | |
74 | >; | |
75 | ||
76 | template <typename T, typename U> | |
77 | using WhenOrderable = void_t < | |
78 | decltype(declval<T>() < declval<U>()), | |
79 | decltype(declval<T>() > declval<U>()), | |
80 | decltype(declval<T>() >= declval<U>()), | |
81 | decltype(declval<T>() <= declval<U>()) | |
82 | >; | |
83 | ||
84 | // Pretend that sizeof(void) is 1, otherwise the in-bounds check doesn't | |
85 | // make sense for `bounded_ptr<void>`. | |
86 | template <typename T> constexpr size_t sizeof_v = sizeof(T); | |
87 | template <> inline constexpr size_t sizeof_v<void> = 1; | |
88 | template <> inline constexpr size_t sizeof_v<void const> = 1; | |
89 | template <> inline constexpr size_t sizeof_v<void volatile> = 1; | |
90 | template <> inline constexpr size_t sizeof_v<void const volatile> = 1; | |
91 | } // end namespace detail | |
92 | ||
93 | // Non-owning pointer to an object (or a range of objects) of type `T` | |
94 | // that validates that the address is within some specified bounds on | |
95 | // dereference-like operations. | |
96 | // | |
97 | // Conceptually, a `bounded_ptr` points within a range of memory `[begin, end)`. | |
98 | // If accessing any part of the result of dereferencing the pointer would | |
99 | // lead to an access outside of the `[begin, end)` range, the pointer is | |
100 | // said to be out-of-bounds. Due to representational constraints, the range | |
101 | // of in-bounds memory must be no larger than 4GB. | |
102 | // | |
103 | // Dereference-like operations (dereference, subscript, pointer member access) | |
104 | // validate that the pointer is not out-of-bounds. If an out-of-bounds pointer | |
105 | // is dereferenced, the `TrappingPolicy` is called as | |
106 | // `TrappingPolicy::trap(some-message)`, and the operation is said to "trap". | |
107 | // This terminology is used below to describe the behavior of the `TrappingPolicy`. | |
108 | // | |
109 | // Pointer arithmetic is allowed (and the bounds are not validated), so it is | |
110 | // entirely possible to make a `bounded_ptr` point outside of its range. | |
111 | // However, overflow checking is performed on arithmetic operations, and | |
112 | // any operation resulting in an overflow will also "trap". | |
113 | // | |
114 | // The behavior of the `TrappingPolicy` can be customized as desired, however | |
115 | // a trap should never return, causing the current `bounded_ptr` operation to | |
116 | // be aborted. This is important since the trap could signify an integer | |
117 | // overflow, a null-pointer dereference or something else that would lead to | |
118 | // undefined behavior (UB) if `TrappingPolicy::trap` were to return. | |
119 | // | |
120 | // Creation of `bounded_ptr`s | |
121 | // ========================== | |
122 | // `bounded_ptr` provides a single constructor allowing the bounds of the | |
123 | // pointer to be specified. When integrating `bounded_ptr` into an existing | |
124 | // code base, it is recommended to use `bounded_ptr` as an iterator obtained | |
125 | // from other container-like abstractions, instead of manually using the | |
126 | // constructor that allows specifying a range. Specifying the range manually | |
127 | // on construction is error-prone, and `bounded_ptr` can't help reduce | |
128 | // out-of-bounds accesses if the bounds are specified incorrectly. | |
129 | // | |
130 | // Furthermore, it is a design choice to not provide a constructor that uses | |
131 | // relative offsets from the pointer itself to determine the range, because | |
132 | // such a constructor is deemed more confusing than helpful. For example, is | |
133 | // the offset a number of bytes or a number of objects? Is the offset inclusive | |
134 | // or exclusive? Instead, factory functions should be used to create `bounded_ptr`s. | |
135 | // | |
136 | // Remark on const-ness | |
137 | // ==================== | |
138 | // Like for raw pointers, the const-ness of a `bounded_ptr` has no bearing on | |
139 | // whether the pointee is const. Hence, it is possible to obtain a non-const | |
140 | // reference to an object from a const `bounded_ptr`. To encode a | |
141 | // pointer-to-const, simply create a `bounded_ptr<T const>`. | |
142 | template <typename T, typename TrappingPolicy> | |
143 | struct __attribute__((trivial_abi)) bounded_ptr { | |
144 | private: | |
145 | using CharType = detail::copy_cv_t<T, char>; | |
146 | ||
147 | public: | |
148 | // Creates a null `bounded_ptr`. | |
149 | // | |
150 | // A null `bounded_ptr` does not point to any object and is conceptually | |
151 | // out of bounds, so dereferencing it will trap. "Observing" operations | |
152 | // like comparison and check-for-null, along with assignment, are valid | |
153 | // operations on a null `bounded_ptr`. | |
154 | OS_ALWAYS_INLINE constexpr | |
155 | bounded_ptr(detail::nullptr_t) | |
156 | : base_(nullptr), count_(0), offset_(0) | |
157 | { | |
158 | } | |
159 | ||
160 | OS_ALWAYS_INLINE constexpr | |
161 | explicit | |
162 | bounded_ptr() | |
163 | : bounded_ptr(nullptr) | |
164 | { | |
165 | } | |
166 | ||
167 | // Creates a `bounded_ptr` pointing to the given object, and whose bounds | |
168 | // are described by the provided `[begin, end)` range. | |
169 | // | |
170 | // This constructor does not check whether the constructed pointer is | |
171 | // within its bounds. However, it does check that the provided `[begin, end)` | |
172 | // range is a valid range (that is, `begin <= end`). | |
173 | // | |
174 | // Furthermore, the number of bytes in the range of in-bounds memory must be | |
175 | // representable by a uint32_t, which means that there can be no more than | |
176 | // 2^32 bytes (i.e. 4GB) in that range. Otherwise, the constructor will trap. | |
177 | OS_ALWAYS_INLINE explicit | |
178 | bounded_ptr(T* pointer, T const* begin, T const* end) | |
179 | { | |
180 | base_ = reinterpret_cast<CharType*>(const_cast<T*>(begin)); | |
181 | ||
182 | // Store (end - begin) into count_, making sure we don't overflow | |
183 | if (__improbable(os_sub_overflow(reinterpret_cast<uintptr_t>(end), | |
184 | reinterpret_cast<uintptr_t>(begin), | |
185 | &count_))) { | |
186 | TrappingPolicy::trap("The range of valid memory is too large to be represented " | |
187 | "by this type, or [begin, end) is not a well-formed range"); | |
188 | } | |
189 | ||
190 | // Store (pointer - begin) into offset_, making sure we don't overflow. | |
191 | // Note that offset_ can be negative if `pointer` is outside of the | |
192 | // range delimited by [begin, end), which can be valid if it represents | |
193 | // e.g. a subrange of an array. | |
194 | if (__improbable(os_sub_overflow(reinterpret_cast<uintptr_t>(pointer), | |
195 | reinterpret_cast<uintptr_t>(begin), | |
196 | &offset_))) { | |
197 | TrappingPolicy::trap("The offset of the pointer inside its valid memory " | |
198 | "range can't be represented using int32_t"); | |
199 | } | |
200 | } | |
201 | ||
202 | // Creates a `bounded_ptr` to a type `T` from a `bounded_ptr` to a type `U`. | |
203 | // | |
204 | // This converting constructor is enabled whenever `U*` is implicitly | |
205 | // convertible to `T*`. This allows the usual implicit conversions | |
206 | // between base-and-derived types, and also from any type `U*` to a | |
207 | // `void*`. If other casts (like between unrelated pointer types) are | |
208 | // desired, `libkern::reinterpret_pointer_cast` can be used instead. | |
209 | // | |
210 | // The bounds on the resulting `bounded_ptr` are inherited from the | |
211 | // original `bounded_ptr`. | |
212 | template <typename U, typename Policy, typename = detail::enable_if_t<detail::is_convertible_v<U*, T*> > > | |
213 | OS_ALWAYS_INLINE | |
214 | bounded_ptr(bounded_ptr<U, Policy> const & other) | |
215 | : base_(other.base_) | |
216 | , count_(other.count_) | |
217 | , offset_(static_cast<int32_t>(reinterpret_cast<CharType*>(static_cast<T*>(other.get_ptr_())) - other.base_)) | |
218 | { | |
219 | } | |
220 | ||
221 | // Assigns a `bounded_ptr` to a type `U` to a `bounded_ptr` to a type `T`, | |
222 | // as long as `U*` is convertible to `T*`. | |
223 | // | |
224 | // This is a rebinding operation, like assignment between raw pointers, | |
225 | // and the destination `bounded_ptr` will inherit the bounds of the | |
226 | // source `bounded_ptr`. | |
227 | template <typename U, typename Policy, typename = detail::enable_if_t<detail::is_convertible_v<U*, T*> > > | |
228 | OS_ALWAYS_INLINE bounded_ptr& | |
229 | operator=(bounded_ptr<U, Policy> const& other) | |
230 | { | |
231 | base_ = other.base_; | |
232 | count_ = other.count_; | |
233 | offset_ = static_cast<int32_t>(reinterpret_cast<CharType*>(static_cast<T*>(other.get_ptr_())) - other.base_); | |
234 | return *this; | |
235 | } | |
236 | ||
237 | // Sets a `bounded_ptr` to null. | |
238 | // | |
239 | // This is effectively equivalent to assigning a default-constructed | |
240 | // `bounded_ptr` to the target. As a result, the original bounds of | |
241 | // the `bounded_ptr` are discarded, and the resulting `bounded_ptr` | |
242 | // is both out-of-bounds and also has no bounds assigned to it (like | |
243 | // a default-constructed `bounded_ptr`). | |
244 | OS_ALWAYS_INLINE bounded_ptr& | |
245 | operator=(detail::nullptr_t) | |
246 | { | |
247 | *this = bounded_ptr(); | |
248 | return *this; | |
249 | } | |
250 | ||
251 | // Returns a reference to the object pointed-to by the `bounded_ptr`. | |
252 | // | |
253 | // Traps if the pointer is pointing outside of its bounds. | |
254 | // | |
255 | // Also note that this function will trap when dereferencing a null | |
256 | // `bounded_ptr`, unless the bounds of the pointer have been set and | |
257 | // include address 0, in which case there's effectively nothing to | |
258 | // diagnose. | |
259 | template <typename T_ = T> // delay instantiation to avoid forming invalid ref for bounded_ptr<void> | |
260 | OS_ALWAYS_INLINE T_& | |
261 | operator*() const | |
262 | { | |
263 | if (__improbable(!in_bounds_())) { | |
264 | TrappingPolicy::trap("bounded_ptr<T>::operator*: Dereferencing this pointer " | |
265 | "would access memory outside of the bounds set originally"); | |
266 | } | |
267 | return *get_ptr_(); | |
268 | } | |
269 | ||
270 | OS_ALWAYS_INLINE T* | |
271 | operator->() const | |
272 | { | |
273 | if (__improbable(!in_bounds_())) { | |
274 | TrappingPolicy::trap("bounded_ptr<T>::operator->: Accessing a member through this pointer " | |
275 | "would access memory outside of the bounds set originally"); | |
276 | } | |
277 | return get_ptr_(); | |
278 | } | |
279 | ||
280 | // Provides access to the n-th element past the given pointer. | |
281 | // | |
282 | // The `bounded_ptr` validates whether the provided index is within the | |
283 | // bounds of the `bounded_ptr`. Like for raw pointers, a negative index | |
284 | // may be passed, in which case the pointer is accessed at a negative | |
285 | // offset (which must still be in bounds). | |
286 | template <typename T_ = T> // delay instantiation to avoid forming invalid ref for bounded_ptr<void> | |
287 | OS_ALWAYS_INLINE T_& | |
288 | operator[](ptrdiff_t n) const | |
289 | { | |
290 | return *(*this + n); | |
291 | } | |
292 | ||
293 | // Converts a `bounded_ptr` to a raw pointer, after checking it is within | |
294 | // its bounds. | |
295 | // | |
296 | // The primary intended usage of this function is to aid bridging between | |
297 | // code that uses `bounded_ptr`s and code that does not. | |
298 | OS_ALWAYS_INLINE T* | |
299 | discard_bounds() const | |
300 | { | |
301 | if (__improbable(!in_bounds_())) { | |
302 | TrappingPolicy::trap("bounded_ptr<T>::discard_bounds: Discarding the bounds on " | |
303 | "this pointer would lose the fact that it is outside of the " | |
304 | "bounds set originally"); | |
305 | } | |
306 | return get_ptr_(); | |
307 | } | |
308 | ||
309 | // Converts a `bounded_ptr` to a raw pointer, without checking whether the | |
310 | // pointer is within its bounds. | |
311 | // | |
312 | // Like `discard_bounds()`, the primary intended usage of this function | |
313 | // is to aid bridging between code that uses `bounded_ptr`s and code that | |
314 | // does not. However, unlike `discard_bounds()`, this function does not | |
315 | // validate that the returned pointer is in bounds. This functionality is | |
316 | // necessary when the pointer represents something that can't be | |
317 | // dereferenced (hence it's OK for it to be out-of-bounds), but that | |
318 | // is still useful for other purposes like comparing against other | |
319 | // pointers. An example of that is the `end` pointer in a half-open | |
320 | // interval `[begin, end)`, where the `end` pointer is out-of-bounds and | |
321 | // can't be dereferenced, yet it's still useful to delimit the range. | |
322 | OS_ALWAYS_INLINE T* | |
323 | unsafe_discard_bounds() const | |
324 | { | |
325 | return get_ptr_(); | |
326 | } | |
327 | ||
328 | // Implicit conversion to bool, returning whether the pointer is null. | |
329 | // | |
330 | // This operation does not perform any validation of the bounds. | |
331 | OS_ALWAYS_INLINE explicit | |
332 | operator bool() const | |
333 | { | |
334 | return get_ptr_() != nullptr; | |
335 | } | |
336 | ||
337 | // Increment/decrement a `bounded_ptr`. | |
338 | // | |
339 | // Like for other arithmetic operations, this does not check whether the | |
340 | // increment or decrement operation results in an out-of-bounds pointer. | |
341 | OS_ALWAYS_INLINE bounded_ptr& | |
342 | operator++() | |
343 | { | |
344 | *this += 1; | |
345 | return *this; | |
346 | } | |
347 | OS_ALWAYS_INLINE bounded_ptr | |
348 | operator++(int) | |
349 | { | |
350 | bounded_ptr old = *this; | |
351 | ++*this; | |
352 | return old; | |
353 | } | |
354 | OS_ALWAYS_INLINE bounded_ptr& | |
355 | operator--() | |
356 | { | |
357 | *this -= 1; | |
358 | return *this; | |
359 | } | |
360 | OS_ALWAYS_INLINE bounded_ptr | |
361 | operator--(int) | |
362 | { | |
363 | bounded_ptr old = *this; | |
364 | --*this; | |
365 | return old; | |
366 | } | |
367 | ||
368 | // Increment or decrement a `bounded_ptr` by a given offset. | |
369 | // | |
370 | // This is equivalent to adding the given offset to the underlying raw | |
371 | // pointer. In particular, the bounds of the `bounded_ptr` are left | |
372 | // untouched by this operation. Furthermore, like for raw pointers, it | |
373 | // is possible to provide a negative offset, which will have the effect | |
374 | // of decrementing the `bounded_ptr` instead of incrementing it. | |
375 | // | |
376 | // Also note that the offset is NOT a number of bytes -- just like for | |
377 | // raw pointers, it is a number of "positions" to move the pointer from, | |
378 | // which essentially means `n * sizeof(T)` bytes. Again, this works exactly | |
379 | // the same as a raw pointer to an object of type `T`. | |
380 | // | |
381 | // Like other arithmetic operations, this does not check whether the | |
382 | // increment or decrement operation results in an out-of-bounds pointer. | |
383 | // However, this does check whether the arithmetic operation would result | |
384 | // in an overflow, in which case the operation will trap. | |
385 | template <typename T_ = T> | |
386 | OS_ALWAYS_INLINE bounded_ptr& | |
387 | operator+=(ptrdiff_t n) | |
388 | { | |
389 | static_assert(!detail::is_void_v<T_>, "Arithmetic on bounded_ptr<void> is not allowed."); | |
390 | ||
391 | ptrdiff_t bytes; | |
392 | if (__improbable(os_mul_overflow(n, sizeof(T), &bytes))) { | |
393 | TrappingPolicy::trap( | |
394 | "bounded_ptr<T>::operator+=(n): Calculating the number of bytes to " | |
395 | "add to the offset (n * sizeof(T)) would trigger an overflow"); | |
396 | } | |
397 | if (__improbable(os_add_overflow(offset_, bytes, &offset_))) { | |
398 | TrappingPolicy::trap( | |
399 | "bounded_ptr<T>::operator+=(n): Adding the specified number of bytes " | |
400 | "to the offset representing the current position would overflow."); | |
401 | } | |
402 | return *this; | |
403 | } | |
404 | ||
405 | template <typename T_ = T> | |
406 | OS_ALWAYS_INLINE bounded_ptr& | |
407 | operator-=(ptrdiff_t n) | |
408 | { | |
409 | static_assert(!detail::is_void_v<T_>, "Arithmetic on bounded_ptr<void> is not allowed."); | |
410 | ||
411 | ptrdiff_t bytes; | |
412 | if (__improbable(os_mul_overflow(n, sizeof(T), &bytes))) { | |
413 | TrappingPolicy::trap( | |
414 | "bounded_ptr<T>::operator-=(n): Calculating the number of bytes to " | |
415 | "subtract from the offset (n * sizeof(T)) would trigger an overflow"); | |
416 | } | |
417 | if (__improbable(os_sub_overflow(offset_, bytes, &offset_))) { | |
418 | TrappingPolicy::trap( | |
419 | "bounded_ptr<T>::operator-=(n): Subtracting the specified number of bytes " | |
420 | "from the offset representing the current position would overflow."); | |
421 | } | |
422 | return *this; | |
423 | } | |
424 | ||
425 | friend OS_ALWAYS_INLINE bounded_ptr | |
426 | operator+(bounded_ptr p, ptrdiff_t n) | |
427 | { | |
428 | p += n; | |
429 | return p; | |
430 | } | |
431 | friend OS_ALWAYS_INLINE bounded_ptr | |
432 | operator+(ptrdiff_t n, bounded_ptr p) | |
433 | { | |
434 | p += n; | |
435 | return p; | |
436 | } | |
437 | friend OS_ALWAYS_INLINE bounded_ptr | |
438 | operator-(bounded_ptr p, ptrdiff_t n) | |
439 | { | |
440 | p -= n; | |
441 | return p; | |
442 | } | |
443 | ||
444 | // Returns the difference between two `bounded_ptr`s. | |
445 | // | |
446 | // This is semantically equivalent to subtracting the two underlying | |
447 | // pointers. The bounds of the pointers are not validated by this | |
448 | // operation. | |
449 | friend OS_ALWAYS_INLINE ptrdiff_t | |
450 | operator-(bounded_ptr const& a, bounded_ptr const& b) | |
451 | { | |
452 | return a.get_ptr_() - b.get_ptr_(); | |
453 | } | |
454 | ||
455 | friend OS_ALWAYS_INLINE ptrdiff_t | |
456 | operator-(bounded_ptr const& a, T const* b) | |
457 | { | |
458 | return a.get_ptr_() - b; | |
459 | } | |
460 | ||
461 | friend OS_ALWAYS_INLINE ptrdiff_t | |
462 | operator-(T const* a, bounded_ptr const& b) | |
463 | { | |
464 | return a - b.get_ptr_(); | |
465 | } | |
466 | ||
467 | private: | |
468 | OS_ALWAYS_INLINE bool | |
469 | in_bounds_() const | |
470 | { | |
471 | static_assert(detail::sizeof_v<T> <= UINT32_MAX - INT32_MAX, | |
472 | "The type pointed-to by bounded_ptr is too large, which would defeat " | |
473 | "our optimization to check for inboundedness using arithmetic on unsigned"); | |
474 | return offset_ >= 0 && static_cast<uint32_t>(offset_) + static_cast<uint32_t>(detail::sizeof_v<T>) <= count_; | |
475 | } | |
476 | ||
477 | OS_ALWAYS_INLINE T* | |
478 | get_ptr_() const | |
479 | { | |
480 | // Compute `base_ + offset_`, catching overflows. | |
481 | uintptr_t ptr; | |
482 | if (__improbable(os_add_overflow(reinterpret_cast<uintptr_t>(base_), offset_, &ptr))) { | |
483 | TrappingPolicy::trap("This bounded_ptr is pointing to memory outside of what can " | |
484 | "be represented by a native pointer."); | |
485 | } | |
486 | return reinterpret_cast<T*>(ptr); | |
487 | } | |
488 | ||
489 | template <typename T_, typename U, typename Policy> | |
490 | friend bounded_ptr<T_, Policy> reinterpret_pointer_cast(bounded_ptr<U, Policy> const&) noexcept; | |
491 | ||
492 | template <typename U, typename P> friend struct bounded_ptr; // for cross-type operations and conversions | |
493 | ||
494 | CharType* base_; // pointer to the beginning of the valid address range | |
495 | uint32_t count_; // number of bytes considered in-bounds (non-negative) | |
496 | int32_t offset_; // current offset into the range, in bytes | |
497 | }; | |
498 | ||
499 | // Returns whether two `bounded_ptr`s point to the same object. | |
500 | // | |
501 | // This comparison is semantically equivalent to comparing the underlying | |
502 | // raw pointers. In particular, it doesn't validate the bounds of either | |
503 | // `bounded_ptr`, nor does it compare whether the two `bounded_ptr`s have | |
504 | // the same bounds. | |
505 | // | |
506 | // This comparison is enabled between `bounded_ptr`s whenever the two | |
507 | // corresponding raw pointer types are comparable. Comparison between a | |
508 | // raw pointer and a `bounded_ptr` is also allowed, so long as the | |
509 | // two corresponding raw pointer types are comparable. | |
510 | template <typename T, typename P1, typename U, typename P2, typename = detail::WhenComparable<T*, U*> > | |
511 | OS_ALWAYS_INLINE bool | |
512 | operator==(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b) | |
513 | { | |
514 | return a.unsafe_discard_bounds() == b.unsafe_discard_bounds(); | |
515 | } | |
516 | ||
517 | template <typename T, typename P1, typename U, typename P2, typename = detail::WhenComparable<T*, U*> > | |
518 | OS_ALWAYS_INLINE bool | |
519 | operator!=(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b) | |
520 | { | |
521 | return !(a == b); | |
522 | } | |
523 | ||
524 | template <typename T, typename P, typename U, typename = detail::WhenComparable<T*, U*> > | |
525 | OS_ALWAYS_INLINE bool | |
526 | operator==(bounded_ptr<T, P> const& a, U* b) | |
527 | { | |
528 | return a.unsafe_discard_bounds() == b; | |
529 | } | |
530 | ||
531 | template <typename T, typename P, typename U, typename = detail::WhenComparable<T*, U*> > | |
532 | OS_ALWAYS_INLINE bool | |
533 | operator==(U* a, bounded_ptr<T, P> const& b) | |
534 | { | |
535 | return a == b.unsafe_discard_bounds(); | |
536 | } | |
537 | ||
538 | template <typename T, typename P, typename U, typename = detail::WhenComparable<T*, U*> > | |
539 | OS_ALWAYS_INLINE bool | |
540 | operator!=(bounded_ptr<T, P> const& a, U* b) | |
541 | { | |
542 | return !(a == b); | |
543 | } | |
544 | ||
545 | template <typename T, typename P, typename U, typename = detail::WhenComparable<T*, U*> > | |
546 | OS_ALWAYS_INLINE bool | |
547 | operator!=(U* a, bounded_ptr<T, P> const& b) | |
548 | { | |
549 | return !(a == b); | |
550 | } | |
551 | ||
552 | template <typename T, typename Policy> | |
553 | OS_ALWAYS_INLINE bool | |
554 | operator==(detail::nullptr_t, bounded_ptr<T, Policy> const& p) | |
555 | { | |
556 | return p.unsafe_discard_bounds() == nullptr; | |
557 | } | |
558 | ||
559 | template <typename T, typename Policy> | |
560 | OS_ALWAYS_INLINE bool | |
561 | operator!=(detail::nullptr_t, bounded_ptr<T, Policy> const& p) | |
562 | { | |
563 | return p.unsafe_discard_bounds() != nullptr; | |
564 | } | |
565 | ||
566 | template <typename T, typename Policy> | |
567 | OS_ALWAYS_INLINE bool | |
568 | operator==(bounded_ptr<T, Policy> const& p, detail::nullptr_t) | |
569 | { | |
570 | return p.unsafe_discard_bounds() == nullptr; | |
571 | } | |
572 | ||
573 | template <typename T, typename Policy> | |
574 | OS_ALWAYS_INLINE bool | |
575 | operator!=(bounded_ptr<T, Policy> const& p, detail::nullptr_t) | |
576 | { | |
577 | return p.unsafe_discard_bounds() != nullptr; | |
578 | } | |
579 | ||
580 | // Returns whether a `bounded_ptr` points to an address that is {less-than, | |
581 | // less-than-or-equal-to, greater-than, greater-than-or-equal-to} the address | |
582 | // held in another `bounded_ptr`. | |
583 | // | |
584 | // This doesn't validate the bounds of either `bounded_ptr`, nor does it | |
585 | // compare those bounds to determine the ordering result. This ordering is | |
586 | // semantically equivalent to ordering the result of calling `get()` on both | |
587 | // `bounded_ptr`s. | |
588 | // | |
589 | // This ordering is enabled between `bounded_ptr`s whenever the two | |
590 | // corresponding raw pointer types are orderable. Ordering between a | |
591 | // raw pointer and a `bounded_ptr` is also allowed, so long as the | |
592 | // two corresponding raw pointer types are orderable. | |
593 | // | |
594 | ||
595 | template <typename T, typename U, typename P1, typename P2, typename = detail::WhenOrderable<T*, U*> > | |
596 | OS_ALWAYS_INLINE bool | |
597 | operator<(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b) | |
598 | { | |
599 | return a.unsafe_discard_bounds() < b.unsafe_discard_bounds(); | |
600 | } | |
601 | ||
602 | template <typename T, typename U, typename P1, typename P2, typename = detail::WhenOrderable<T*, U*> > | |
603 | OS_ALWAYS_INLINE bool | |
604 | operator<=(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b) | |
605 | { | |
606 | return a.unsafe_discard_bounds() <= b.unsafe_discard_bounds(); | |
607 | } | |
608 | ||
609 | template <typename T, typename U, typename P1, typename P2, typename = detail::WhenOrderable<T*, U*> > | |
610 | OS_ALWAYS_INLINE bool | |
611 | operator>(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b) | |
612 | { | |
613 | return a.unsafe_discard_bounds() > b.unsafe_discard_bounds(); | |
614 | } | |
615 | ||
616 | template <typename T, typename U, typename P1, typename P2, typename = detail::WhenOrderable<T*, U*> > | |
617 | OS_ALWAYS_INLINE bool | |
618 | operator>=(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b) | |
619 | { | |
620 | return a.unsafe_discard_bounds() >= b.unsafe_discard_bounds(); | |
621 | } | |
622 | ||
623 | template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> > | |
624 | OS_ALWAYS_INLINE bool | |
625 | operator<(T* a, bounded_ptr<U, P> const& b) | |
626 | { | |
627 | return a < b.unsafe_discard_bounds(); | |
628 | } | |
629 | ||
630 | template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> > | |
631 | OS_ALWAYS_INLINE bool | |
632 | operator<(bounded_ptr<T, P> const& a, U* b) | |
633 | { | |
634 | return a.unsafe_discard_bounds() < b; | |
635 | } | |
636 | ||
637 | template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> > | |
638 | OS_ALWAYS_INLINE bool | |
639 | operator<=(T* a, bounded_ptr<U, P> const& b) | |
640 | { | |
641 | return a <= b.unsafe_discard_bounds(); | |
642 | } | |
643 | ||
644 | template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> > | |
645 | OS_ALWAYS_INLINE bool | |
646 | operator<=(bounded_ptr<T, P> const& a, U* b) | |
647 | { | |
648 | return a.unsafe_discard_bounds() <= b; | |
649 | } | |
650 | ||
651 | template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> > | |
652 | OS_ALWAYS_INLINE bool | |
653 | operator>(T* a, bounded_ptr<U, P> const& b) | |
654 | { | |
655 | return a > b.unsafe_discard_bounds(); | |
656 | } | |
657 | ||
658 | template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> > | |
659 | OS_ALWAYS_INLINE bool | |
660 | operator>(bounded_ptr<T, P> const& a, U* b) | |
661 | { | |
662 | return a.unsafe_discard_bounds() > b; | |
663 | } | |
664 | ||
665 | template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> > | |
666 | OS_ALWAYS_INLINE bool | |
667 | operator>=(T* a, bounded_ptr<U, P> const& b) | |
668 | { | |
669 | return a >= b.unsafe_discard_bounds(); | |
670 | } | |
671 | ||
672 | template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> > | |
673 | OS_ALWAYS_INLINE bool | |
674 | operator>=(bounded_ptr<T, P> const& a, U* b) | |
675 | { | |
676 | return a.unsafe_discard_bounds() >= b; | |
677 | } | |
678 | ||
679 | template <typename T, typename U> | |
680 | OS_ALWAYS_INLINE T* | |
681 | reinterpret_pointer_cast(U* p) noexcept | |
682 | { | |
683 | return reinterpret_cast<T*>(p); | |
684 | } | |
685 | ||
686 | // Reinterprets a `bounded_ptr` to a type `T` to a `bounded_ptr` to a type `U`. | |
687 | // | |
688 | // This is equivalent to `reinterpret_cast`ing the underlying pointer as well | |
689 | // as the bounds of the original pointer. Like for a raw `reinterpret_cast`, | |
690 | // no offset adjustment is performed (even if needed, e.g. for derived-to-base | |
691 | // casts with multiple inheritance). Because this is extremely unsafe, it should | |
692 | // be used extremely sparingly. | |
693 | template <typename T, typename U, typename Policy> | |
694 | OS_ALWAYS_INLINE bounded_ptr<T, Policy> | |
695 | reinterpret_pointer_cast(bounded_ptr<U, Policy> const& p) noexcept | |
696 | { | |
697 | using CharType = detail::copy_cv_t<T, char>; | |
698 | CharType* new_begin = reinterpret_cast<CharType*>(p.base_); | |
699 | CharType* new_end = new_begin + p.count_; | |
700 | return bounded_ptr<T, Policy>(reinterpret_cast<T*>(p.get_ptr_()), | |
701 | reinterpret_cast<T const*>(new_begin), | |
702 | reinterpret_cast<T const*>(new_end)); | |
703 | } | |
704 | } // end namespace libkern | |
705 | ||
706 | #endif // !XNU_LIBKERN_LIBKERN_CXX_BOUNDED_PTR_H |