// Copyright 2022 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_MEMORY_RAW_REF_H_ #define BASE_MEMORY_RAW_REF_H_ #include #include #include #include "base/compiler_specific.h" #include "base/memory/raw_ptr.h" #include "third_party/abseil-cpp/absl/base/attributes.h" namespace base { template class raw_ref; namespace internal { template struct is_raw_ref : std::false_type {}; template struct is_raw_ref<::base::raw_ref> : std::true_type {}; template constexpr inline bool is_raw_ref_v = is_raw_ref::value; } // namespace internal // A smart pointer for a pointer which can not be null, and which provides // Use-after-Free protection in the same ways as raw_ptr. This class acts like a // combination of std::reference_wrapper and raw_ptr. // // See raw_ptr and //base/memory/raw_ptr.md for more details on the // Use-after-Free protection. // // # Use after move // // The raw_ref type will abort if used after being moved. // // # Constness // // Use a `const raw_ref` when the smart pointer should not be able to rebind // to a new reference. Use a `const raw_ref` do the same for a const // reference, which is like `const T&`. // // Unlike a native `T&` reference, a mutable `raw_ref` can be changed // independent of the underlying `T`, similar to `std::reference_wrapper`. That // means the reference inside it can be moved and reassigned. template class TRIVIAL_ABI GSL_POINTER raw_ref { using Inner = raw_ptr; // These impls do not clear on move, which produces an inconsistent behaviour. // We want consistent behaviour such that using a raw_ref after move is caught // and aborts. Failure to clear would be indicated by the related death tests // not CHECKing appropriately. static constexpr bool need_clear_after_move = std::is_same_v || #if defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS) std::is_same_v> || #endif // defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS) std::is_same_v; public: ALWAYS_INLINE explicit raw_ref(T& p) noexcept : inner_(std::addressof(p)) {} ALWAYS_INLINE raw_ref& operator=(T& p) noexcept { inner_.operator=(&p); return *this; } // Disallow holding references to temporaries. raw_ref(const T&& p) = delete; raw_ref& operator=(const T&& p) = delete; ALWAYS_INLINE raw_ref(const raw_ref& p) noexcept : inner_(p.inner_) { CHECK(inner_.get()); // Catch use-after-move. } ALWAYS_INLINE raw_ref(raw_ref&& p) noexcept : inner_(std::move(p.inner_)) { CHECK(inner_.get()); // Catch use-after-move. if constexpr (need_clear_after_move) p.inner_ = nullptr; } ALWAYS_INLINE raw_ref& operator=(const raw_ref& p) noexcept { CHECK(p.inner_.get()); // Catch use-after-move. inner_.operator=(p.inner_); return *this; } ALWAYS_INLINE raw_ref& operator=(raw_ref&& p) noexcept { CHECK(p.inner_.get()); // Catch use-after-move. inner_.operator=(std::move(p.inner_)); if constexpr (need_clear_after_move) p.inner_ = nullptr; return *this; } // Deliberately implicit in order to support implicit upcast. template >> // NOLINTNEXTLINE(google-explicit-constructor) ALWAYS_INLINE raw_ref(const raw_ref& p) noexcept : inner_(p.inner_) { CHECK(inner_.get()); // Catch use-after-move. } // Deliberately implicit in order to support implicit upcast. template >> // NOLINTNEXTLINE(google-explicit-constructor) ALWAYS_INLINE raw_ref(raw_ref&& p) noexcept : inner_(std::move(p.inner_)) { CHECK(inner_.get()); // Catch use-after-move. if constexpr (need_clear_after_move) p.inner_ = nullptr; } // Upcast assignment template >> ALWAYS_INLINE raw_ref& operator=(const raw_ref& p) noexcept { CHECK(p.inner_.get()); // Catch use-after-move. inner_.operator=(p.inner_); return *this; } template >> ALWAYS_INLINE raw_ref& operator=(raw_ref&& p) noexcept { CHECK(p.inner_.get()); // Catch use-after-move. inner_.operator=(std::move(p.inner_)); if constexpr (need_clear_after_move) p.inner_ = nullptr; return *this; } ALWAYS_INLINE T& operator*() const { CHECK(inner_.get()); // Catch use-after-move. return inner_.operator*(); } ALWAYS_INLINE T* operator->() const ABSL_ATTRIBUTE_RETURNS_NONNULL { CHECK(inner_.get()); // Catch use-after-move. return inner_.operator->(); } friend ALWAYS_INLINE void swap(raw_ref& lhs, raw_ref& rhs) noexcept { CHECK(lhs.inner_.get()); // Catch use-after-move. CHECK(rhs.inner_.get()); // Catch use-after-move. swap(lhs.inner_, rhs.inner_); } // If T can be serialised into trace, its alias is also // serialisable. template typename perfetto::check_traced_value_support::type WriteIntoTrace( perfetto::TracedValue&& context) const { CHECK(inner_.get()); // Catch use-after-move. inner_.WriteIntoTrace(std::move(context)); } template friend ALWAYS_INLINE bool operator==(const raw_ref& lhs, const raw_ref& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. CHECK(rhs.inner_.get()); // Catch use-after-move. return lhs.inner_ == rhs.inner_; } template friend ALWAYS_INLINE bool operator!=(const raw_ref& lhs, const raw_ref& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. CHECK(rhs.inner_.get()); // Catch use-after-move. return lhs.inner_ != rhs.inner_; } template friend ALWAYS_INLINE bool operator<(const raw_ref& lhs, const raw_ref& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. CHECK(rhs.inner_.get()); // Catch use-after-move. return lhs.inner_ < rhs.inner_; } template friend ALWAYS_INLINE bool operator>(const raw_ref& lhs, const raw_ref& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. CHECK(rhs.inner_.get()); // Catch use-after-move. return lhs.inner_ > rhs.inner_; } template friend ALWAYS_INLINE bool operator<=(const raw_ref& lhs, const raw_ref& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. CHECK(rhs.inner_.get()); // Catch use-after-move. return lhs.inner_ <= rhs.inner_; } template friend ALWAYS_INLINE bool operator>=(const raw_ref& lhs, const raw_ref& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. CHECK(rhs.inner_.get()); // Catch use-after-move. return lhs.inner_ >= rhs.inner_; } template , void>> friend ALWAYS_INLINE bool operator==(const raw_ref& lhs, const U& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. return lhs.inner_ == &rhs; } template , void>> friend ALWAYS_INLINE bool operator!=(const raw_ref& lhs, const U& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. return lhs.inner_ != &rhs; } template , void>> friend ALWAYS_INLINE bool operator<(const raw_ref& lhs, const U& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. return lhs.inner_ < &rhs; } template , void>> friend ALWAYS_INLINE bool operator>(const raw_ref& lhs, const U& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. return lhs.inner_ > &rhs; } template , void>> friend ALWAYS_INLINE bool operator<=(const raw_ref& lhs, const U& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. return lhs.inner_ <= &rhs; } template , void>> friend ALWAYS_INLINE bool operator>=(const raw_ref& lhs, const U& rhs) { CHECK(lhs.inner_.get()); // Catch use-after-move. return lhs.inner_ >= &rhs; } template , void>> friend ALWAYS_INLINE bool operator==(const U& lhs, const raw_ref& rhs) { CHECK(rhs.inner_.get()); // Catch use-after-move. return &lhs == rhs.inner_; } template , void>> friend ALWAYS_INLINE bool operator!=(const U& lhs, const raw_ref& rhs) { CHECK(rhs.inner_.get()); // Catch use-after-move. return &lhs != rhs.inner_; } template , void>> friend ALWAYS_INLINE bool operator<(const U& lhs, const raw_ref& rhs) { CHECK(rhs.inner_.get()); // Catch use-after-move. return &lhs < rhs.inner_; } template , void>> friend ALWAYS_INLINE bool operator>(const U& lhs, const raw_ref& rhs) { CHECK(rhs.inner_.get()); // Catch use-after-move. return &lhs > rhs.inner_; } template , void>> friend ALWAYS_INLINE bool operator<=(const U& lhs, const raw_ref& rhs) { CHECK(rhs.inner_.get()); // Catch use-after-move. return &lhs <= rhs.inner_; } template , void>> friend ALWAYS_INLINE bool operator>=(const U& lhs, const raw_ref& rhs) { CHECK(rhs.inner_.get()); // Catch use-after-move. return &lhs >= rhs.inner_; } private: template friend class raw_ref; Inner inner_; }; // CTAD deduction guide. template raw_ref(T) -> raw_ref; // Template helpers for working with raw_ref. template struct IsRawRef : std::false_type {}; template struct IsRawRef> : std::true_type {}; template inline constexpr bool IsRawRefV = IsRawRef::value; template struct RemoveRawRef { using type = T; }; template struct RemoveRawRef> { using type = T; }; template using RemoveRawRefT = typename RemoveRawRef::type; } // namespace base using base::raw_ref; namespace std { // Override so set/map lookups do not create extra raw_ref. This also // allows C++ references to be used for lookup. template struct less> { using is_transparent = void; bool operator()(const raw_ref& lhs, const raw_ref& rhs) const { Impl::IncrementLessCountForTest(); return lhs < rhs; } bool operator()(T& lhs, const raw_ref& rhs) const { Impl::IncrementLessCountForTest(); return lhs < rhs; } bool operator()(const raw_ref& lhs, T& rhs) const { Impl::IncrementLessCountForTest(); return lhs < rhs; } }; } // namespace std #endif // BASE_MEMORY_RAW_REF_H_