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