1 // Copyright 2022 The Chromium Authors. All rights reserved. 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_MEMORY_RAW_PTR_ASAN_BOUND_ARG_TRACKER_H_ 6 #define BASE_MEMORY_RAW_PTR_ASAN_BOUND_ARG_TRACKER_H_ 7 8 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" 9 10 #if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) 11 #include <cstddef> 12 #include <cstdint> 13 #include <memory> 14 #include <vector> 15 16 #include "base/base_export.h" 17 #include "base/containers/stack_container.h" 18 #include "base/memory/raw_ptr.h" 19 20 namespace base { 21 namespace internal { 22 template <typename, typename> 23 struct Invoker; 24 25 template <typename T, typename UnretainedTrait, RawPtrTraits PtrTraits> 26 class UnretainedWrapper; 27 28 template <typename T, typename UnretainedTrait, RawPtrTraits PtrTraits> 29 class UnretainedRefWrapper; 30 } // namespace internal 31 32 // Tracks the lifetimes of bound pointer arguments during callback invocation. 33 // 34 // Example: 35 // T* unsafe_ptr = new T(); 36 // PostTask(base::BindOnce(&T::DoSomething, base::Unretained(unsafe_ptr))); 37 // delete unsafe_ptr; 38 // 39 // When the callback executes, the callee has no access to the raw_ptr<T> inside 40 // base::Unretained, so it is not possible for it to be invalidated until the 41 // callback finishes execution; so there is always at least one live raw_ptr<T> 42 // pointing to `this` for the duration of the call to T::DoSomething. 43 // 44 // This class is responsible for tracking and checking which allocations are 45 // currently protected in this way, and it is only intended to be used inside 46 // the Bind implementation. This should not be used directly. 47 class BASE_EXPORT RawPtrAsanBoundArgTracker { 48 public: 49 static constexpr size_t kInlineArgsCount = 3; 50 using ProtectedArgsVector = base::StackVector<uintptr_t, kInlineArgsCount>; 51 52 // Check whether ptr is an address inside an allocation pointed to by one of 53 // the currently protected callback arguments. If it is, then this function 54 // returns the base address of that allocation, otherwise it returns 0. 55 static uintptr_t GetProtectedArgPtr(uintptr_t ptr); 56 57 private: 58 template <typename, typename> 59 friend struct internal::Invoker; 60 61 void Add(uintptr_t pointer); 62 63 RawPtrAsanBoundArgTracker(); 64 ~RawPtrAsanBoundArgTracker(); 65 66 // Base case for any type that isn't base::Unretained, we do nothing. 67 template <typename T> AddArg(const T & arg)68 void AddArg(const T& arg) {} 69 70 // No specialization for raw_ptr<T> directly, since bound raw_ptr<T> 71 // arguments are stored in UnretainedWrapper. 72 73 // When argument is base::Unretained, add the argument to the set of 74 // arguments protected in this scope. 75 template <typename T, typename UnretainedTrait, RawPtrTraits PtrTraits> AddArg(const internal::UnretainedWrapper<T,UnretainedTrait,PtrTraits> & arg)76 void AddArg( 77 const internal::UnretainedWrapper<T, UnretainedTrait, PtrTraits>& arg) { 78 if constexpr (raw_ptr_traits::IsSupportedType<T>::value) { 79 auto inner = arg.get(); 80 // The argument may unwrap into a raw_ptr or a T* depending if it is 81 // allowed to dangle. 82 if constexpr (IsRawPtrV<decltype(inner)>) { 83 Add(reinterpret_cast<uintptr_t>(inner.get())); 84 } else { 85 Add(reinterpret_cast<uintptr_t>(inner)); 86 } 87 } 88 } 89 90 // When argument is a reference type that's supported by raw_ptr, add the 91 // argument to the set of arguments protected in this scope. 92 template <typename T, typename UnretainedTrait, RawPtrTraits PtrTraits> AddArg(const internal::UnretainedRefWrapper<T,UnretainedTrait,PtrTraits> & arg)93 void AddArg( 94 const internal::UnretainedRefWrapper<T, UnretainedTrait, PtrTraits>& 95 arg) { 96 if constexpr (raw_ptr_traits::IsSupportedType<T>::value) { 97 Add(reinterpret_cast<uintptr_t>(&arg.get())); 98 } 99 } 100 101 template <typename... Args> AddArgs(Args &&...args)102 void AddArgs(Args&&... args) { 103 if (enabled_) { 104 (AddArg(std::forward<Args>(args)), ...); 105 } 106 } 107 108 // Cache whether or not BRP-ASan is running when we enter the argument 109 // tracking scope so that we ensure that our actions on leaving the scope are 110 // consistent even if the runtime flags are changed. 111 bool enabled_; 112 113 // We save the previously bound arguments, so that we can restore them when 114 // this callback returns. This helps with coverage while avoiding false 115 // positives due to nested run loops/callback re-entrancy. 116 raw_ptr<ProtectedArgsVector> prev_protected_args_; 117 ProtectedArgsVector protected_args_; 118 }; 119 120 } // namespace base 121 122 #endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) 123 #endif // BASE_MEMORY_RAW_PTR_ASAN_BOUND_ARG_TRACKER_H_ 124