xref: /xnu-11215/iokit/DriverKit/bounded_ptr.h (revision 4f1223e8)
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