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