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 PARTITION_ALLOC_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_
6 #define PARTITION_ALLOC_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_
7 
8 #include <cstddef>
9 #include <type_traits>
10 
11 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
12 #include "partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h"
13 #include "partition_alloc/partition_alloc_buildflags.h"
14 #include "partition_alloc/partition_alloc_forward.h"
15 
16 #if !BUILDFLAG(USE_ASAN_UNOWNED_PTR)
17 #error "Included under wrong build option"
18 #endif
19 
20 namespace base::internal {
21 
22 bool EndOfAliveAllocation(const volatile void* ptr, bool is_adjustable_ptr);
23 bool LikelySmuggledScalar(const volatile void* ptr);
24 
25 template <bool IsAdjustablePtr, bool MayDangle>
26 struct RawPtrAsanUnownedImpl {
27   // The first two are needed for correctness. The last one isn't technically a
28   // must, but better to set it.
29   static constexpr bool kMustZeroOnConstruct = true;
30   static constexpr bool kMustZeroOnMove = true;
31   static constexpr bool kMustZeroOnDestruct = true;
32 
33   // Wraps a pointer.
34   template <typename T>
WrapRawPtrRawPtrAsanUnownedImpl35   PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) {
36     return ptr;
37   }
38 
39   // Notifies the allocator when a wrapped pointer is being removed or replaced.
40   template <typename T>
ReleaseWrappedPtrRawPtrAsanUnownedImpl41   PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* wrapped_ptr) {
42     if (!partition_alloc::internal::base::is_constant_evaluated()) {
43       ProbeForLowSeverityLifetimeIssue(wrapped_ptr);
44     }
45   }
46 
47   // Unwraps the pointer, while asserting that memory hasn't been freed. The
48   // function is allowed to crash on nullptr.
49   template <typename T>
SafelyUnwrapPtrForDereferenceRawPtrAsanUnownedImpl50   PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference(
51       T* wrapped_ptr) {
52     // ASAN will catch use of dereferenced ptr without additional probing.
53     return wrapped_ptr;
54   }
55 
56   // Unwraps the pointer, while asserting that memory hasn't been freed. The
57   // function must handle nullptr gracefully.
58   template <typename T>
SafelyUnwrapPtrForExtractionRawPtrAsanUnownedImpl59   PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction(
60       T* wrapped_ptr) {
61     if (!partition_alloc::internal::base::is_constant_evaluated()) {
62       ProbeForLowSeverityLifetimeIssue(wrapped_ptr);
63     }
64     return wrapped_ptr;
65   }
66 
67   // Unwraps the pointer, without making an assertion on whether memory was
68   // freed or not.
69   template <typename T>
UnsafelyUnwrapPtrForComparisonRawPtrAsanUnownedImpl70   PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison(
71       T* wrapped_ptr) {
72     return wrapped_ptr;
73   }
74 
75   // Upcasts the wrapped pointer.
76   template <typename To, typename From>
UpcastRawPtrAsanUnownedImpl77   PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) {
78     static_assert(std::is_convertible_v<From*, To*>,
79                   "From must be convertible to To.");
80     // Note, this cast may change the address if upcasting to base that lies in
81     // the middle of the derived object.
82     return wrapped_ptr;
83   }
84 
85   // Advance the wrapped pointer by `delta_elems`.
86   template <
87       typename T,
88       typename Z,
89       typename =
90           std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>>
91   PA_ALWAYS_INLINE static constexpr T*
AdvanceRawPtrAsanUnownedImpl92   Advance(T* wrapped_ptr, Z delta_elems, bool /*is_in_pointer_modification*/) {
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>>
102   PA_ALWAYS_INLINE static constexpr T*
RetreatRawPtrAsanUnownedImpl103   Retreat(T* wrapped_ptr, Z delta_elems, bool /*is_in_pointer_modification*/) {
104     return wrapped_ptr - delta_elems;
105   }
106 
107   template <typename T>
GetDeltaElemsRawPtrAsanUnownedImpl108   PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1,
109                                                             T* wrapped_ptr2) {
110     return wrapped_ptr1 - wrapped_ptr2;
111   }
112 
113   // Returns a copy of a wrapped pointer, without making an assertion on whether
114   // memory was freed or not.
115   template <typename T>
DuplicateRawPtrAsanUnownedImpl116   PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) {
117     return wrapped_ptr;
118   }
119 
120   template <typename T>
ProbeForLowSeverityLifetimeIssueRawPtrAsanUnownedImpl121   static void ProbeForLowSeverityLifetimeIssue(T* wrapped_ptr) {
122     if (!MayDangle && wrapped_ptr) {
123       const volatile void* probe_ptr =
124           reinterpret_cast<const volatile void*>(wrapped_ptr);
125       if (!LikelySmuggledScalar(probe_ptr) &&
126           !EndOfAliveAllocation(probe_ptr, IsAdjustablePtr)) {
127         reinterpret_cast<const volatile uint8_t*>(probe_ptr)[0];
128       }
129     }
130   }
131 
132   // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used
133   // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor.
134   template <typename T>
WrapRawPtrForDuplicationRawPtrAsanUnownedImpl135   PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) {
136     return ptr;
137   }
138 
139   template <typename T>
UnsafelyUnwrapPtrForDuplicationRawPtrAsanUnownedImpl140   PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication(
141       T* wrapped_ptr) {
142     return wrapped_ptr;
143   }
144 
145   template <typename T>
TraceRawPtrAsanUnownedImpl146   static constexpr void Trace(uint64_t owner_id, T* wrapped_ptr) {}
UntraceRawPtrAsanUnownedImpl147   static constexpr void Untrace(uint64_t owner_id) {}
148 
149   // This is for accounting only, used by unit tests.
IncrementSwapCountForTestRawPtrAsanUnownedImpl150   PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {}
IncrementLessCountForTestRawPtrAsanUnownedImpl151   PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {}
152 };
153 
154 }  // namespace base::internal
155 
156 #endif  // PARTITION_ALLOC_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_
157