1 // Copyright 2018 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_OBSERVER_LIST_INTERNAL_H_ 6 #define BASE_OBSERVER_LIST_INTERNAL_H_ 7 8 #include <string> 9 10 #include "base/base_export.h" 11 #include "base/check.h" 12 #include "base/containers/linked_list.h" 13 #include "base/dcheck_is_on.h" 14 #include "base/memory/raw_ptr.h" 15 #include "base/memory/raw_ptr_exclusion.h" 16 #include "base/memory/weak_ptr.h" 17 #include "base/observer_list_types.h" 18 19 #if DCHECK_IS_ON() 20 #include "base/debug/stack_trace.h" 21 #endif 22 23 namespace base { 24 namespace internal { 25 26 // Adapter for putting raw pointers into an ObserverList<Foo>::Unchecked. 27 template <base::RawPtrTraits ptr_traits = RawPtrTraits::kEmpty> 28 class BASE_EXPORT UncheckedObserverAdapter { 29 public: UncheckedObserverAdapter(const void * observer)30 explicit UncheckedObserverAdapter(const void* observer) 31 : ptr_(const_cast<void*>(observer)) {} 32 UncheckedObserverAdapter(const UncheckedObserverAdapter&) = delete; 33 UncheckedObserverAdapter& operator=(const UncheckedObserverAdapter&) = delete; 34 UncheckedObserverAdapter(UncheckedObserverAdapter&& other) = default; 35 UncheckedObserverAdapter& operator=(UncheckedObserverAdapter&& other) = 36 default; 37 MarkForRemoval()38 void MarkForRemoval() { ptr_ = nullptr; } 39 IsMarkedForRemoval()40 bool IsMarkedForRemoval() const { return !ptr_; } IsEqual(const void * rhs)41 bool IsEqual(const void* rhs) const { return ptr_ == rhs; } 42 43 template <class ObserverType> Get(const UncheckedObserverAdapter & adapter)44 static ObserverType* Get(const UncheckedObserverAdapter& adapter) { 45 static_assert( 46 !std::is_base_of_v<CheckedObserver, ObserverType>, 47 "CheckedObserver classes must not use ObserverList<T>::Unchecked."); 48 return static_cast<ObserverType*>(adapter.ptr_); 49 } 50 51 #if DCHECK_IS_ON() GetCreationStackString()52 std::string GetCreationStackString() const { 53 return "Observer created at:\n" + stack_.ToString(); 54 } 55 #endif // DCHECK_IS_ON() 56 57 private: 58 raw_ptr<void, ptr_traits> ptr_; 59 #if DCHECK_IS_ON() 60 base::debug::StackTrace stack_; 61 #endif // DCHECK_IS_ON() 62 }; 63 64 // Adapter for CheckedObserver types so that they can use the same syntax as a 65 // raw pointer when stored in the std::vector of observers in an ObserverList. 66 // It wraps a WeakPtr<CheckedObserver> and allows a "null" pointer via 67 // destruction to be distinguished from an observer marked for deferred removal 68 // whilst an iteration is in progress. 69 class BASE_EXPORT CheckedObserverAdapter { 70 public: 71 explicit CheckedObserverAdapter(const CheckedObserver* observer); 72 73 // Move-only construction and assignment is required to store this in STL 74 // types. 75 CheckedObserverAdapter(CheckedObserverAdapter&& other); 76 CheckedObserverAdapter& operator=(CheckedObserverAdapter&& other); 77 CheckedObserverAdapter(const CheckedObserverAdapter&) = delete; 78 CheckedObserverAdapter& operator=(const CheckedObserverAdapter&) = delete; 79 ~CheckedObserverAdapter(); 80 MarkForRemoval()81 void MarkForRemoval() { 82 DCHECK(weak_ptr_); 83 weak_ptr_ = nullptr; 84 } 85 IsMarkedForRemoval()86 bool IsMarkedForRemoval() const { 87 // If |weak_ptr_| was invalidated then this attempt to iterate over the 88 // pointer is a UAF. Tip: If it's unclear where the `delete` occurred, try 89 // adding CHECK(!IsInObserverList()) to the ~CheckedObserver() (destructor) 90 // override. However, note that this is not always a bug: a destroyed 91 // observer can exist in an ObserverList so long as nothing iterates over 92 // the ObserverList before the list itself is destroyed. 93 CHECK(!weak_ptr_.WasInvalidated()); 94 return weak_ptr_ == nullptr; 95 } 96 IsEqual(const CheckedObserver * rhs)97 bool IsEqual(const CheckedObserver* rhs) const { 98 // Note that inside an iteration, ObserverList::HasObserver() may call this 99 // and |weak_ptr_| may be null due to a deferred removal, which is fine. 100 return weak_ptr_.get() == rhs; 101 } 102 103 template <class ObserverType> Get(const CheckedObserverAdapter & adapter)104 static ObserverType* Get(const CheckedObserverAdapter& adapter) { 105 static_assert( 106 std::is_base_of_v<CheckedObserver, ObserverType>, 107 "Observers should inherit from base::CheckedObserver. " 108 "Use ObserverList<T>::Unchecked to observe with raw pointers."); 109 DCHECK(adapter.weak_ptr_); 110 return static_cast<ObserverType*>(adapter.weak_ptr_.get()); 111 } 112 113 #if DCHECK_IS_ON() GetCreationStackString()114 std::string GetCreationStackString() const { return stack_.ToString(); } 115 #endif 116 117 private: 118 WeakPtr<CheckedObserver> weak_ptr_; 119 #if DCHECK_IS_ON() 120 base::debug::StackTrace stack_; 121 #endif 122 }; 123 124 // Wraps a pointer in a stack-allocated, base::LinkNode. The node is 125 // automatically removed from the linked list upon destruction (of the node, not 126 // the pointer). Nodes are detached from the list via Invalidate() in the 127 // destructor of ObserverList. This invalidates all WeakLinkNodes. There is no 128 // threading support. 129 template <class ObserverList> 130 class WeakLinkNode : public base::LinkNode<WeakLinkNode<ObserverList>> { 131 public: 132 WeakLinkNode() = default; WeakLinkNode(ObserverList * list)133 explicit WeakLinkNode(ObserverList* list) { SetList(list); } 134 WeakLinkNode(const WeakLinkNode&) = delete; 135 WeakLinkNode& operator=(const WeakLinkNode&) = delete; 136 ~WeakLinkNode()137 ~WeakLinkNode() { Invalidate(); } 138 IsOnlyRemainingNode()139 bool IsOnlyRemainingNode() const { 140 return list_ && 141 list_->live_iterators_.head() == list_->live_iterators_.tail(); 142 } 143 SetList(ObserverList * list)144 void SetList(ObserverList* list) { 145 DCHECK(!list_); 146 DCHECK(list); 147 list_ = list; 148 list_->live_iterators_.Append(this); 149 } 150 Invalidate()151 void Invalidate() { 152 if (list_) { 153 list_ = nullptr; 154 this->RemoveFromList(); 155 } 156 } 157 get()158 ObserverList* get() const { 159 #if EXPENSIVE_DCHECKS_ARE_ON() 160 if (list_) 161 DCHECK_CALLED_ON_VALID_SEQUENCE(list_->iteration_sequence_checker_); 162 #endif // EXPENSIVE_DCHECKS_ARE_ON() 163 return list_; 164 } 165 ObserverList* operator->() const { return get(); } 166 explicit operator bool() const { return get(); } 167 168 private: 169 // `list_` is not a raw_ptr<...> for performance reasons: on-stack pointer + 170 // based on analysis of sampling profiler data and tab_search:top100:2020. 171 RAW_PTR_EXCLUSION ObserverList* list_ = nullptr; 172 }; 173 174 } // namespace internal 175 } // namespace base 176 177 #endif // BASE_OBSERVER_LIST_INTERNAL_H_ 178