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