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