• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_REF_H_
6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_REF_H_
7 
8 #include <memory>
9 #include <type_traits>
10 #include <utility>
11 
12 #include "base/allocator/partition_allocator/partition_alloc_base/augmentations/compiler_specific.h"
13 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
14 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
15 #include "base/allocator/partition_allocator/partition_alloc_config.h"
16 #include "base/allocator/partition_allocator/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 Traits = RawPtrTraits::kEmpty>
57 class PA_TRIVIAL_ABI PA_GSL_POINTER raw_ref {
58   // operator* is used with the expectation of GetForExtraction semantics:
59   //
60   // raw_ref<Foo> foo_raw_ref = something;
61   // Foo& foo_ref = *foo_raw_ref;
62   //
63   // The implementation of operator* provides GetForDereference semantics, and
64   // this results in spurious crashes in BRP-ASan builds, so we need to disable
65   // hooks that provide BRP-ASan instrumentation for raw_ref.
66   using Inner = raw_ptr<T, Traits | RawPtrTraits::kDisableHooks>;
67 
68   // Some underlying implementations do not clear on move, which produces an
69   // inconsistent behaviour. We want consistent behaviour such that using a
70   // raw_ref after move is caught and aborts, so do it when the underlying
71   // implementation doesn't. Failure to clear would be indicated by the related
72   // death tests not CHECKing appropriately.
73   static constexpr bool kNeedClearAfterMove = !Inner::kZeroOnMove;
74 
75  public:
76   using Impl = typename Inner::Impl;
77 
78   // Construct a raw_ref from a pointer, which must not be null.
79   //
80   // This function is safe to use with any pointer, as it will CHECK and
81   // terminate the process if the pointer is null. Avoid dereferencing a pointer
82   // to avoid this CHECK as you may be dereferencing null.
83   PA_ALWAYS_INLINE constexpr static raw_ref from_ptr(T* ptr) noexcept {
84     PA_RAW_PTR_CHECK(ptr);
85     return raw_ref(*ptr);
86   }
87 
88   // Construct a raw_ref from a reference.
89   PA_ALWAYS_INLINE constexpr explicit raw_ref(T& p) noexcept
90       : inner_(std::addressof(p)) {}
91 
92   // Assign a new reference to the raw_ref, replacing the existing reference.
93   PA_ALWAYS_INLINE constexpr raw_ref& operator=(T& p) noexcept {
94     inner_.operator=(&p);
95     return *this;
96   }
97 
98   // Disallow holding references to temporaries.
99   raw_ref(const T&& p) = delete;
100   raw_ref& operator=(const T&& p) = delete;
101 
102   PA_ALWAYS_INLINE constexpr raw_ref(const raw_ref& p) noexcept
103       : inner_(p.inner_) {
104     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
105   }
106 
107   PA_ALWAYS_INLINE constexpr raw_ref(raw_ref&& p) noexcept
108       : inner_(std::move(p.inner_)) {
109     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
110     if constexpr (kNeedClearAfterMove) {
111       p.inner_ = nullptr;
112     }
113   }
114 
115   PA_ALWAYS_INLINE constexpr raw_ref& operator=(const raw_ref& p) noexcept {
116     PA_RAW_PTR_CHECK(p.inner_);  // Catch use-after-move.
117     inner_.operator=(p.inner_);
118     return *this;
119   }
120 
121   PA_ALWAYS_INLINE constexpr raw_ref& operator=(raw_ref&& p) noexcept {
122     PA_RAW_PTR_CHECK(p.inner_);  // Catch use-after-move.
123     inner_.operator=(std::move(p.inner_));
124     if constexpr (kNeedClearAfterMove) {
125       p.inner_ = nullptr;
126     }
127     return *this;
128   }
129 
130   // Deliberately implicit in order to support implicit upcast.
131   // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
132   // allow it.
133   template <class U,
134             RawPtrTraits PassedTraits,
135             class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
136   // NOLINTNEXTLINE(google-explicit-constructor)
137   PA_ALWAYS_INLINE constexpr raw_ref(const raw_ref<U, PassedTraits>& p) noexcept
138       : inner_(p.inner_) {
139     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
140   }
141   // Deliberately implicit in order to support implicit upcast.
142   // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
143   // allow it.
144   template <class U,
145             RawPtrTraits PassedTraits,
146             class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
147   // NOLINTNEXTLINE(google-explicit-constructor)
148   PA_ALWAYS_INLINE constexpr raw_ref(raw_ref<U, PassedTraits>&& p) noexcept
149       : inner_(std::move(p.inner_)) {
150     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
151     if constexpr (kNeedClearAfterMove) {
152       p.inner_ = nullptr;
153     }
154   }
155 
156   // Upcast assignment
157   // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
158   // allow it.
159   template <class U,
160             RawPtrTraits PassedTraits,
161             class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
162   PA_ALWAYS_INLINE constexpr raw_ref& operator=(
163       const raw_ref<U, PassedTraits>& p) noexcept {
164     PA_RAW_PTR_CHECK(p.inner_);  // Catch use-after-move.
165     inner_.operator=(p.inner_);
166     return *this;
167   }
168   // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
169   // allow it.
170   template <class U,
171             RawPtrTraits PassedTraits,
172             class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
173   PA_ALWAYS_INLINE constexpr raw_ref& operator=(
174       raw_ref<U, PassedTraits>&& p) noexcept {
175     PA_RAW_PTR_CHECK(p.inner_);  // Catch use-after-move.
176     inner_.operator=(std::move(p.inner_));
177     if constexpr (kNeedClearAfterMove) {
178       p.inner_ = nullptr;
179     }
180     return *this;
181   }
182 
183   PA_ALWAYS_INLINE constexpr T& operator*() const {
184     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
185     return inner_.operator*();
186   }
187 
188   // This is an equivalent to operator*() that provides GetForExtraction rather
189   // rather than GetForDereference semantics (see raw_ptr.h). This should be
190   // used in place of operator*() when the memory referred to by the reference
191   // is not immediately going to be accessed.
192   PA_ALWAYS_INLINE constexpr T& get() const {
193     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
194     return *inner_.get();
195   }
196 
197   PA_ALWAYS_INLINE constexpr T* operator->() const
198       PA_ATTRIBUTE_RETURNS_NONNULL {
199     PA_RAW_PTR_CHECK(inner_);  // Catch use-after-move.
200     return inner_.operator->();
201   }
202 
203   // This is used to verify callbacks are not invoked with dangling references.
204   // If the `raw_ref` references a deleted object, it will trigger an error.
205   // Depending on the PartitionAllocUnretainedDanglingPtr feature, this is
206   // either a DumpWithoutCrashing, a crash, or ignored.
207   PA_ALWAYS_INLINE void ReportIfDangling() const noexcept {
208     inner_.ReportIfDangling();
209   }
210 
211   PA_ALWAYS_INLINE friend constexpr void swap(raw_ref& lhs,
212                                               raw_ref& rhs) noexcept {
213     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
214     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
215     swap(lhs.inner_, rhs.inner_);
216   }
217 
218   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
219   friend bool operator==(const raw_ref<U, Traits1>& lhs,
220                          const raw_ref<V, Traits2>& rhs);
221   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
222   friend bool operator!=(const raw_ref<U, Traits1>& lhs,
223                          const raw_ref<V, Traits2>& rhs);
224   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
225   friend bool operator<(const raw_ref<U, Traits1>& lhs,
226                         const raw_ref<V, Traits2>& rhs);
227   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
228   friend bool operator>(const raw_ref<U, Traits1>& lhs,
229                         const raw_ref<V, Traits2>& rhs);
230   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
231   friend bool operator<=(const raw_ref<U, Traits1>& lhs,
232                          const raw_ref<V, Traits2>& rhs);
233   template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
234   friend bool operator>=(const raw_ref<U, Traits1>& lhs,
235                          const raw_ref<V, Traits2>& rhs);
236 
237   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
238   PA_ALWAYS_INLINE friend bool operator==(const raw_ref& lhs, const U& rhs) {
239     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
240     return lhs.inner_ == &rhs;
241   }
242   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
243   PA_ALWAYS_INLINE friend bool operator!=(const raw_ref& lhs, const U& rhs) {
244     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
245     return lhs.inner_ != &rhs;
246   }
247   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
248   PA_ALWAYS_INLINE friend bool operator<(const raw_ref& lhs, const U& rhs) {
249     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
250     return lhs.inner_ < &rhs;
251   }
252   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
253   PA_ALWAYS_INLINE friend bool operator>(const raw_ref& lhs, const U& rhs) {
254     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
255     return lhs.inner_ > &rhs;
256   }
257   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
258   PA_ALWAYS_INLINE friend bool operator<=(const raw_ref& lhs, const U& rhs) {
259     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
260     return lhs.inner_ <= &rhs;
261   }
262   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
263   PA_ALWAYS_INLINE friend bool operator>=(const raw_ref& lhs, const U& rhs) {
264     PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
265     return lhs.inner_ >= &rhs;
266   }
267 
268   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
269   PA_ALWAYS_INLINE friend bool operator==(const U& lhs, const raw_ref& rhs) {
270     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
271     return &lhs == rhs.inner_;
272   }
273   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
274   PA_ALWAYS_INLINE friend bool operator!=(const U& lhs, const raw_ref& rhs) {
275     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
276     return &lhs != rhs.inner_;
277   }
278   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
279   PA_ALWAYS_INLINE friend bool operator<(const U& lhs, const raw_ref& rhs) {
280     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
281     return &lhs < rhs.inner_;
282   }
283   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
284   PA_ALWAYS_INLINE friend bool operator>(const U& lhs, const raw_ref& rhs) {
285     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
286     return &lhs > rhs.inner_;
287   }
288   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
289   PA_ALWAYS_INLINE friend bool operator<=(const U& lhs, const raw_ref& rhs) {
290     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
291     return &lhs <= rhs.inner_;
292   }
293   template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
294   PA_ALWAYS_INLINE friend bool operator>=(const U& lhs, const raw_ref& rhs) {
295     PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
296     return &lhs >= rhs.inner_;
297   }
298 
299  private:
300   template <class U, RawPtrTraits R>
301   friend class raw_ref;
302 
303   Inner inner_;
304 };
305 
306 template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
307 PA_ALWAYS_INLINE bool operator==(const raw_ref<U, Traits1>& lhs,
308                                  const raw_ref<V, Traits2>& rhs) {
309   PA_RAW_PTR_CHECK(lhs.inner_);  // Catch use-after-move.
310   PA_RAW_PTR_CHECK(rhs.inner_);  // Catch use-after-move.
311   return lhs.inner_ == rhs.inner_;
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 
349 // CTAD deduction guide.
350 template <class T>
351 raw_ref(T&) -> raw_ref<T>;
352 template <class T>
353 raw_ref(const T&) -> raw_ref<const T>;
354 
355 // Template helpers for working with raw_ref<T>.
356 template <typename T>
357 struct IsRawRef : std::false_type {};
358 
359 template <typename T, RawPtrTraits Traits>
360 struct IsRawRef<raw_ref<T, Traits>> : std::true_type {};
361 
362 template <typename T>
363 inline constexpr bool IsRawRefV = IsRawRef<T>::value;
364 
365 template <typename T>
366 struct RemoveRawRef {
367   using type = T;
368 };
369 
370 template <typename T, RawPtrTraits Traits>
371 struct RemoveRawRef<raw_ref<T, Traits>> {
372   using type = T;
373 };
374 
375 template <typename T>
376 using RemoveRawRefT = typename RemoveRawRef<T>::type;
377 
378 }  // namespace base
379 
380 using base::raw_ref;
381 
382 namespace std {
383 
384 // Override so set/map lookups do not create extra raw_ref. This also
385 // allows C++ references to be used for lookup.
386 template <typename T, base::RawPtrTraits Traits>
387 struct less<raw_ref<T, Traits>> {
388   using Impl = typename raw_ref<T, Traits>::Impl;
389   using is_transparent = void;
390 
391   bool operator()(const raw_ref<T, Traits>& lhs,
392                   const raw_ref<T, Traits>& rhs) const {
393     Impl::IncrementLessCountForTest();
394     return lhs < rhs;
395   }
396 
397   bool operator()(T& lhs, const raw_ref<T, Traits>& rhs) const {
398     Impl::IncrementLessCountForTest();
399     return lhs < rhs;
400   }
401 
402   bool operator()(const raw_ref<T, Traits>& lhs, T& rhs) const {
403     Impl::IncrementLessCountForTest();
404     return lhs < rhs;
405   }
406 };
407 
408 #if defined(_LIBCPP_VERSION)
409 // Specialize std::pointer_traits. The latter is required to obtain the
410 // underlying raw pointer in the std::to_address(pointer) overload.
411 // Implementing the pointer_traits is the standard blessed way to customize
412 // `std::to_address(pointer)` in C++20 [3].
413 //
414 // [1] https://wg21.link/pointer.traits.optmem
415 
416 template <typename T, ::base::RawPtrTraits Traits>
417 struct pointer_traits<::raw_ref<T, Traits>> {
418   using pointer = ::raw_ref<T, Traits>;
419   using element_type = T;
420   using difference_type = ptrdiff_t;
421 
422   template <typename U>
423   using rebind = ::raw_ref<U, Traits>;
424 
425   static constexpr pointer pointer_to(element_type& r) noexcept {
426     return pointer(r);
427   }
428 
429   static constexpr element_type* to_address(pointer p) noexcept {
430     // `raw_ref::get` is used instead of raw_ref::operator*`. It provides
431     // GetForExtraction rather rather than GetForDereference semantics (see
432     // raw_ptr.h). This should be used when we we don't know the memory will be
433     // accessed.
434     return &(p.get());
435   }
436 };
437 #endif  // defined(_LIBCPP_VERSION)
438 
439 }  // namespace std
440 
441 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_REF_H_
442