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