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