• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_
6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_
7 
8 #include <stddef.h>
9 
10 #include <type_traits>
11 
12 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
13 #include "base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h"
14 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
15 #include "base/allocator/partition_allocator/partition_alloc_forward.h"
16 
17 #if !BUILDFLAG(USE_ASAN_UNOWNED_PTR)
18 #error "Included under wrong build option"
19 #endif
20 
21 namespace base::internal {
22 
23 bool EndOfAliveAllocation(const volatile void* ptr, bool is_adjustable_ptr);
24 bool LikelySmuggledScalar(const volatile void* ptr);
25 
26 template <bool IsAdjustablePtr>
27 struct RawPtrAsanUnownedImpl {
28   // The first two are needed for correctness. The last one isn't technically a
29   // must, but better to set it.
30   static constexpr bool kMustZeroOnInit = true;
31   static constexpr bool kMustZeroOnMove = true;
32   static constexpr bool kMustZeroOnDestruct = true;
33 
34   // Wraps a pointer.
35   template <typename T>
WrapRawPtrRawPtrAsanUnownedImpl36   PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) {
37     return ptr;
38   }
39 
40   // Notifies the allocator when a wrapped pointer is being removed or replaced.
41   template <typename T>
ReleaseWrappedPtrRawPtrAsanUnownedImpl42   PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* wrapped_ptr) {
43     if (!partition_alloc::internal::base::is_constant_evaluated()) {
44       ProbeForLowSeverityLifetimeIssue(wrapped_ptr);
45     }
46   }
47 
48   // Unwraps the pointer, while asserting that memory hasn't been freed. The
49   // function is allowed to crash on nullptr.
50   template <typename T>
SafelyUnwrapPtrForDereferenceRawPtrAsanUnownedImpl51   PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference(
52       T* wrapped_ptr) {
53     // ASAN will catch use of dereferenced ptr without additional probing.
54     return wrapped_ptr;
55   }
56 
57   // Unwraps the pointer, while asserting that memory hasn't been freed. The
58   // function must handle nullptr gracefully.
59   template <typename T>
SafelyUnwrapPtrForExtractionRawPtrAsanUnownedImpl60   PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction(
61       T* wrapped_ptr) {
62     if (!partition_alloc::internal::base::is_constant_evaluated()) {
63       ProbeForLowSeverityLifetimeIssue(wrapped_ptr);
64     }
65     return wrapped_ptr;
66   }
67 
68   // Unwraps the pointer, without making an assertion on whether memory was
69   // freed or not.
70   template <typename T>
UnsafelyUnwrapPtrForComparisonRawPtrAsanUnownedImpl71   PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison(
72       T* wrapped_ptr) {
73     return wrapped_ptr;
74   }
75 
76   // Upcasts the wrapped pointer.
77   template <typename To, typename From>
UpcastRawPtrAsanUnownedImpl78   PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) {
79     static_assert(std::is_convertible<From*, To*>::value,
80                   "From must be convertible to To.");
81     // Note, this cast may change the address if upcasting to base that lies in
82     // the middle of the derived object.
83     return wrapped_ptr;
84   }
85 
86   // Advance the wrapped pointer by `delta_elems`.
87   template <
88       typename T,
89       typename Z,
90       typename =
91           std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>>
AdvanceRawPtrAsanUnownedImpl92   PA_ALWAYS_INLINE static constexpr T* Advance(T* wrapped_ptr, Z delta_elems) {
93     return wrapped_ptr + delta_elems;
94   }
95 
96   // Retreat the wrapped pointer by `delta_elems`.
97   template <
98       typename T,
99       typename Z,
100       typename =
101           std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>>
RetreatRawPtrAsanUnownedImpl102   PA_ALWAYS_INLINE static constexpr T* Retreat(T* wrapped_ptr, Z delta_elems) {
103     return wrapped_ptr - delta_elems;
104   }
105 
106   template <typename T>
GetDeltaElemsRawPtrAsanUnownedImpl107   PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1,
108                                                             T* wrapped_ptr2) {
109     return wrapped_ptr1 - wrapped_ptr2;
110   }
111 
112   // Returns a copy of a wrapped pointer, without making an assertion on whether
113   // memory was freed or not.
114   template <typename T>
DuplicateRawPtrAsanUnownedImpl115   PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) {
116     return wrapped_ptr;
117   }
118 
119   template <typename T>
ProbeForLowSeverityLifetimeIssueRawPtrAsanUnownedImpl120   static void ProbeForLowSeverityLifetimeIssue(T* wrapped_ptr) {
121     if (wrapped_ptr) {
122       const volatile void* probe_ptr =
123           reinterpret_cast<const volatile void*>(wrapped_ptr);
124       if (!LikelySmuggledScalar(probe_ptr) &&
125           !EndOfAliveAllocation(probe_ptr, IsAdjustablePtr)) {
126         reinterpret_cast<const volatile uint8_t*>(probe_ptr)[0];
127       }
128     }
129   }
130 
131   // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used
132   // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor.
133   template <typename T>
WrapRawPtrForDuplicationRawPtrAsanUnownedImpl134   PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) {
135     return ptr;
136   }
137 
138   template <typename T>
UnsafelyUnwrapPtrForDuplicationRawPtrAsanUnownedImpl139   PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication(
140       T* wrapped_ptr) {
141     return wrapped_ptr;
142   }
143 
144   // This is for accounting only, used by unit tests.
IncrementSwapCountForTestRawPtrAsanUnownedImpl145   PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {}
IncrementLessCountForTestRawPtrAsanUnownedImpl146   PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {}
147   PA_ALWAYS_INLINE static constexpr void
IncrementPointerToMemberOperatorCountForTestRawPtrAsanUnownedImpl148   IncrementPointerToMemberOperatorCountForTest() {}
149 };
150 
151 }  // namespace base::internal
152 
153 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_
154