xref: /aosp_15_r20/external/cronet/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ref.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef PARTITION_ALLOC_POINTERS_RAW_REF_H_
6 #define PARTITION_ALLOC_POINTERS_RAW_REF_H_
7 
8 #include <memory>
9 #include <type_traits>
10 #include <utility>
11 
12 #include "partition_alloc/partition_alloc_base/augmentations/compiler_specific.h"
13 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
14 #include "partition_alloc/partition_alloc_buildflags.h"
15 #include "partition_alloc/partition_alloc_config.h"
16 #include "partition_alloc/pointers/raw_ptr.h"
17 
18 namespace base {
19 
20 template <class T, RawPtrTraits Traits>
21 class raw_ref;
22 
23 namespace internal {
24 
25 template <class T>
26 struct is_raw_ref : std::false_type {};
27 
28 template <class T, RawPtrTraits Traits>
29 struct is_raw_ref<::base::raw_ref<T, Traits>> : std::true_type {};
30 
31 template <class T>
32 constexpr inline bool is_raw_ref_v = is_raw_ref<T>::value;
33 
34 }  // namespace internal
35 
36 // A smart pointer for a pointer which can not be null, and which provides
37 // Use-after-Free protection in the same ways as raw_ptr. This class acts like a
38 // combination of std::reference_wrapper and raw_ptr.
39 //
40 // See raw_ptr and //base/memory/raw_ptr.md for more details on the
41 // Use-after-Free protection.
42 //
43 // # Use after move
44 //
45 // The raw_ref type will abort if used after being moved.
46 //
47 // # Constness
48 //
49 // Use a `const raw_ref<T>` when the smart pointer should not be able to rebind
50 // to a new reference. Use a `const raw_ref<const T>` do the same for a const
51 // reference, which is like `const T&`.
52 //
53 // Unlike a native `T&` reference, a mutable `raw_ref<T>` can be changed
54 // independent of the underlying `T`, similar to `std::reference_wrapper`. That
55 // means the reference inside it can be moved and reassigned.
56 template <class T, RawPtrTraits ReferenceTraits = RawPtrTraits::kEmpty>
57 class PA_TRIVIAL_ABI PA_GSL_POINTER raw_ref {
58  public:
59   // Users may specify `RawPtrTraits` via raw_ref's second template parameter
60   // `ReferenceTraits`, or specialization of `raw_ptr_traits::kTypeTraits<T>`.
61   constexpr static auto Traits =
62       ReferenceTraits | raw_ptr_traits::kTypeTraits<T>;
63 
64  private:
65   // operator* is used with the expectation of GetForExtraction semantics:
66   //
67   // raw_ref<Foo> foo_raw_ref = something;
68   // Foo& foo_ref = *foo_raw_ref;
69   //
70   // The implementation of operator* provides GetForDereference semantics, and
71   // this results in spurious crashes in BRP-ASan builds, so we need to disable
72   // hooks that provide BRP-ASan instrumentation for raw_ref.
73   using Inner = raw_ptr<T, Traits | RawPtrTraits::kDisableHooks>;
74 
75   // Some underlying implementations do not clear on move, which produces an
76   // inconsistent behaviour. We want consistent behaviour such that using a
77   // raw_ref after move is caught and aborts, so do it when the underlying
78   // implementation doesn't. Failure to clear would be indicated by the related
79   // death tests not CHECKing appropriately.
80   static constexpr bool kNeedClearAfterMove = !Inner::kZeroOnMove;
81 
82  public:
83   using Impl = typename Inner::Impl;
84 
85   // Construct a raw_ref from a pointer, which must not be null.
86   //
87   // This function is safe to use with any pointer, as it will CHECK and
88   // terminate the process if the pointer is null. Avoid dereferencing a pointer
89   // to avoid this CHECK as you may be dereferencing null.
90   PA_ALWAYS_INLINE constexpr static raw_ref from_ptr(T* ptr) noexcept {
91     PA_RAW_PTR_CHECK(ptr);
92     return raw_ref(*ptr);
93   }
94 
95   // Construct a raw_ref from a reference.
96   PA_ALWAYS_INLINE constexpr explicit raw_ref(T& p) noexcept
97       : inner_(std::addressof(p)) {}
98 
99   // Assign a new reference to the raw_ref, replacing the existing reference.
100   PA_ALWAYS_INLINE constexpr raw_ref& operator=(T& p) noexcept {
101     inner_.operator=(&p);
102     return *this;
103   }
104 
105   // Disallow holding references to temporaries.
106   raw_ref(const T&& p) = delete;
107   raw_ref& operator=(const T&& p) = delete;
108 
109   PA_ALWAYS_INLINE constexpr raw_ref(const raw_ref& p) noexcept
110       : inner_(p.inner_) {
111     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
112   }
113 
114   PA_ALWAYS_INLINE constexpr raw_ref(raw_ref&& p) noexcept
115       : inner_(std::move(p.inner_)) {
116     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
117     if constexpr (kNeedClearAfterMove) {
118       p.inner_ = nullptr;
119     }
120   }
121 
122   PA_ALWAYS_INLINE constexpr raw_ref& operator=(const raw_ref& p) noexcept {
123     PA_RAW_PTR_CHECK(p.inner_);  // Catch use-after-move.
124     inner_.operator=(p.inner_);
125     return *this;
126   }
127 
128   PA_ALWAYS_INLINE constexpr raw_ref& operator=(raw_ref&& p) noexcept {
129     PA_RAW_PTR_CHECK(p.inner_);  // Catch use-after-move.
130     inner_.operator=(std::move(p.inner_));
131     if constexpr (kNeedClearAfterMove) {
132       p.inner_ = nullptr;
133     }
134     return *this;
135   }
136 
137   // Deliberately implicit in order to support implicit upcast.
138   // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
139   // allow it.
140   template <class U,
141             RawPtrTraits PassedTraits,
142             class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
143   // NOLINTNEXTLINE(google-explicit-constructor)
144   PA_ALWAYS_INLINE constexpr raw_ref(const raw_ref<U, PassedTraits>& p) noexcept
145       : inner_(p.inner_) {
146     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
147   }
148   // Deliberately implicit in order to support implicit upcast.
149   // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
150   // allow it.
151   template <class U,
152             RawPtrTraits PassedTraits,
153             class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
154   // NOLINTNEXTLINE(google-explicit-constructor)
155   PA_ALWAYS_INLINE constexpr raw_ref(raw_ref<U, PassedTraits>&& p) noexcept
156       : inner_(std::move(p.inner_)) {
157     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
158     if constexpr (kNeedClearAfterMove) {
159       p.inner_ = nullptr;
160     }
161   }
162 
163   // Upcast assignment
164   // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
165   // allow it.
166   template <class U,
167             RawPtrTraits PassedTraits,
168             class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
169   PA_ALWAYS_INLINE constexpr raw_ref& operator=(
170       const raw_ref<U, PassedTraits>& p) noexcept {
171     PA_RAW_PTR_CHECK(p.inner_);  // Catch use-after-move.
172     inner_.operator=(p.inner_);
173     return *this;
174   }
175   // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
176   // allow it.
177   template <class U,
178             RawPtrTraits PassedTraits,
179             class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
180   PA_ALWAYS_INLINE constexpr raw_ref& operator=(
181       raw_ref<U, PassedTraits>&& p) noexcept {
182     PA_RAW_PTR_CHECK(p.inner_);  // Catch use-after-move.
183     inner_.operator=(std::move(p.inner_));
184     if constexpr (kNeedClearAfterMove) {
185       p.inner_ = nullptr;
186     }
187     return *this;
188   }
189 
190   PA_ALWAYS_INLINE constexpr T& operator*() const {
191     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
192     return inner_.operator*();
193   }
194 
195   // This is an equivalent to operator*() that provides GetForExtraction rather
196   // rather than GetForDereference semantics (see raw_ptr.h). This should be
197   // used in place of operator*() when the memory referred to by the reference
198   // is not immediately going to be accessed.
199   PA_ALWAYS_INLINE constexpr T& get() const {
200     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
201     return *inner_.get();
202   }
203 
204   PA_ALWAYS_INLINE constexpr T* operator->() const
205       PA_ATTRIBUTE_RETURNS_NONNULL {
206     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
207     return inner_.operator->();
208   }
209 
210   // This is used to verify callbacks are not invoked with dangling references.
211   // If the `raw_ref` references a deleted object, it will trigger an error.
212   // Depending on the PartitionAllocUnretainedDanglingPtr feature, this is
213   // either a DumpWithoutCrashing, a crash, or ignored.
214   PA_ALWAYS_INLINE void ReportIfDangling() const noexcept {
215     inner_.ReportIfDangling();
216   }
217 
218   PA_ALWAYS_INLINE friend constexpr void swap(raw_ref& lhs,
219                                               raw_ref& rhs) noexcept {
220     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
221     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
222     swap(lhs.inner_, rhs.inner_);
223   }
224 
225   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
226   friend bool operator==(const raw_ref<U, Traits1>& lhs,
227                          const raw_ref<V, Traits2>& rhs);
228   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
229   friend bool operator!=(const raw_ref<U, Traits1>& lhs,
230                          const raw_ref<V, Traits2>& rhs);
231   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
232   friend bool operator<(const raw_ref<U, Traits1>& lhs,
233                         const raw_ref<V, Traits2>& rhs);
234   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
235   friend bool operator>(const raw_ref<U, Traits1>& lhs,
236                         const raw_ref<V, Traits2>& rhs);
237   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
238   friend bool operator<=(const raw_ref<U, Traits1>& lhs,
239                          const raw_ref<V, Traits2>& rhs);
240   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
241   friend bool operator>=(const raw_ref<U, Traits1>& lhs,
242                          const raw_ref<V, Traits2>& rhs);
243 
244   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
245   PA_ALWAYS_INLINE friend bool operator==(const raw_ref& lhs, const U& rhs) {
246     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
247     return lhs.inner_ == &rhs;
248   }
249   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
250   PA_ALWAYS_INLINE friend bool operator!=(const raw_ref& lhs, const U& rhs) {
251     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
252     return lhs.inner_ != &rhs;
253   }
254   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
255   PA_ALWAYS_INLINE friend bool operator<(const raw_ref& lhs, const U& rhs) {
256     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
257     return lhs.inner_ < &rhs;
258   }
259   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
260   PA_ALWAYS_INLINE friend bool operator>(const raw_ref& lhs, const U& rhs) {
261     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
262     return lhs.inner_ > &rhs;
263   }
264   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
265   PA_ALWAYS_INLINE friend bool operator<=(const raw_ref& lhs, const U& rhs) {
266     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
267     return lhs.inner_ <= &rhs;
268   }
269   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
270   PA_ALWAYS_INLINE friend bool operator>=(const raw_ref& lhs, const U& rhs) {
271     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
272     return lhs.inner_ >= &rhs;
273   }
274 
275   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
276   PA_ALWAYS_INLINE friend bool operator==(const U& lhs, const raw_ref& rhs) {
277     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
278     return &lhs == rhs.inner_;
279   }
280   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
281   PA_ALWAYS_INLINE friend bool operator!=(const U& lhs, const raw_ref& rhs) {
282     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
283     return &lhs != rhs.inner_;
284   }
285   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
286   PA_ALWAYS_INLINE friend bool operator<(const U& lhs, const raw_ref& rhs) {
287     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
288     return &lhs < rhs.inner_;
289   }
290   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
291   PA_ALWAYS_INLINE friend bool operator>(const U& lhs, const raw_ref& rhs) {
292     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
293     return &lhs > rhs.inner_;
294   }
295   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
296   PA_ALWAYS_INLINE friend bool operator<=(const U& lhs, const raw_ref& rhs) {
297     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
298     return &lhs <= rhs.inner_;
299   }
300   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
301   PA_ALWAYS_INLINE friend bool operator>=(const U& lhs, const raw_ref& rhs) {
302     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
303     return &lhs >= rhs.inner_;
304   }
305 
306  private:
307   template <class U, RawPtrTraits R>
308   friend class raw_ref;
309 
310   Inner inner_;
311 };
312 
313 template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
314 PA_ALWAYS_INLINE bool operator==(const raw_ref<U, Traits1>& lhs,
315                                  const raw_ref<V, Traits2>& rhs) {
316   PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
317   PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
318   return lhs.inner_ == rhs.inner_;
319 }
320 template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
321 PA_ALWAYS_INLINE bool operator!=(const raw_ref<U, Traits1>& lhs,
322                                  const raw_ref<V, Traits2>& rhs) {
323   PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
324   PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
325   return lhs.inner_ != rhs.inner_;
326 }
327 template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
328 PA_ALWAYS_INLINE bool operator<(const raw_ref<U, Traits1>& lhs,
329                                 const raw_ref<V, Traits2>& rhs) {
330   PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
331   PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
332   return lhs.inner_ < rhs.inner_;
333 }
334 template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
335 PA_ALWAYS_INLINE bool operator>(const raw_ref<U, Traits1>& lhs,
336                                 const raw_ref<V, Traits2>& rhs) {
337   PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
338   PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
339   return lhs.inner_ > rhs.inner_;
340 }
341 template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
342 PA_ALWAYS_INLINE bool operator<=(const raw_ref<U, Traits1>& lhs,
343                                  const raw_ref<V, Traits2>& rhs) {
344   PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
345   PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
346   return lhs.inner_ <= rhs.inner_;
347 }
348 template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
349 PA_ALWAYS_INLINE bool operator>=(const raw_ref<U, Traits1>& lhs,
350                                  const raw_ref<V, Traits2>& rhs) {
351   PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
352   PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
353   return lhs.inner_ >= rhs.inner_;
354 }
355 
356 // CTAD deduction guide.
357 template <class T>
358 raw_ref(T&) -> raw_ref<T>;
359 template <class T>
360 raw_ref(const T&) -> raw_ref<const T>;
361 
362 // Template helpers for working with raw_ref<T>.
363 template <typename T>
364 struct IsRawRef : std::false_type {};
365 
366 template <typename T, RawPtrTraits Traits>
367 struct IsRawRef<raw_ref<T, Traits>> : std::true_type {};
368 
369 template <typename T>
370 inline constexpr bool IsRawRefV = IsRawRef<T>::value;
371 
372 template <typename T>
373 struct RemoveRawRef {
374   using type = T;
375 };
376 
377 template <typename T, RawPtrTraits Traits>
378 struct RemoveRawRef<raw_ref<T, Traits>> {
379   using type = T;
380 };
381 
382 template <typename T>
383 using RemoveRawRefT = typename RemoveRawRef<T>::type;
384 
385 }  // namespace base
386 
387 using base::raw_ref;
388 
389 template <base::RawPtrTraits Traits = base::RawPtrTraits::kEmpty, typename T>
390 auto ToRawRef(T& ref) {
391   return raw_ref<T, Traits>(ref);
392 }
393 
394 namespace std {
395 
396 // Override so set/map lookups do not create extra raw_ref. This also
397 // allows C++ references to be used for lookup.
398 template <typename T, base::RawPtrTraits Traits>
399 struct less<raw_ref<T, Traits>> {
400   using Impl = typename raw_ref<T, Traits>::Impl;
401   using is_transparent = void;
402 
403   bool operator()(const raw_ref<T, Traits>& lhs,
404                   const raw_ref<T, Traits>& rhs) const {
405     Impl::IncrementLessCountForTest();
406     return lhs < rhs;
407   }
408 
409   bool operator()(T& lhs, const raw_ref<T, Traits>& rhs) const {
410     Impl::IncrementLessCountForTest();
411     return lhs < rhs;
412   }
413 
414   bool operator()(const raw_ref<T, Traits>& lhs, T& rhs) const {
415     Impl::IncrementLessCountForTest();
416     return lhs < rhs;
417   }
418 };
419 
420 // Specialize std::pointer_traits. The latter is required to obtain the
421 // underlying raw pointer in the std::to_address(pointer) overload.
422 // Implementing the pointer_traits is the standard blessed way to customize
423 // `std::to_address(pointer)` in C++20 [3].
424 //
425 // [1] https://wg21.link/pointer.traits.optmem
426 
427 template <typename T, ::base::RawPtrTraits Traits>
428 struct pointer_traits<::raw_ref<T, Traits>> {
429   using pointer = ::raw_ref<T, Traits>;
430   using element_type = T;
431   using difference_type = ptrdiff_t;
432 
433   template <typename U>
434   using rebind = ::raw_ref<U, Traits>;
435 
436   static constexpr pointer pointer_to(element_type& r) noexcept {
437     return pointer(r);
438   }
439 
440   static constexpr element_type* to_address(pointer p) noexcept {
441     // `raw_ref::get` is used instead of raw_ref::operator*`. It provides
442     // GetForExtraction rather rather than GetForDereference semantics (see
443     // raw_ptr.h). This should be used when we we don't know the memory will be
444     // accessed.
445     return &(p.get());
446   }
447 };
448 
449 }  // namespace std
450 
451 #endif  // PARTITION_ALLOC_POINTERS_RAW_REF_H_
452