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_MEMORY_SAFETY_CHECKS_H_
6 #define BASE_MEMORY_SAFETY_CHECKS_H_
7
8 #include <new>
9 #include <type_traits>
10
11 #include "base/compiler_specific.h"
12 #include "base/dcheck_is_on.h"
13 #include "partition_alloc/buildflags.h"
14
15 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
16 #include "partition_alloc/partition_alloc_constants.h" // nogncheck
17 #endif
18
19 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
20 #include "partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h"
21 #endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
22
23 // This header defines `ADVANCED_MEMORY_SAFETY_CHECKS()` macro.
24 // They can be used to specify a class/struct that is targeted to perform
25 // additional CHECKS across variety of memory safety mechanisms such as
26 // PartitionAllocator.
27 // ```
28 // class Foo {
29 // ADVANCED_MEMORY_SAFETY_CHECKS();
30 // }
31 // ```
32 // Checks here are disabled by default because of their performance cost.
33 // Currently, the macro is managed by the memory safety team internally and
34 // you should not add / remove it manually.
35 //
36 // Additional checks here are categorized into either one of enum
37 // `MemorySafetyCheck`. Some of them are too costly and disabled even for
38 // `ADVANCED_MEMORY_SAFETY_CHECKS()` annotated types. These checks can be
39 // enabled by passing optional arguments to the macro.
40 // ```
41 // class Foo {
42 // ADVANCED_MEMORY_SAFETY_CHECKS(
43 // /*enable=*/ kFoo | kBar);
44 // }
45 // ```
46 // It is also possible to disable default checks for annotated types.
47 // ```
48 // class Foo {
49 // ADVANCED_MEMORY_SAFETY_CHECKS(
50 // /*enable=*/ kFoo,
51 // /*disable=*/ kBaz);
52 // }
53 // ```
54
55 // We cannot hide things behind anonymous namespace because they are referenced
56 // via macro, which can be defined anywhere.
57 // To avoid tainting ::base namespace, define things inside this namespace.
58 namespace base::internal {
59
60 enum class MemorySafetyCheck : uint32_t {
61 kNone = 0,
62 kForcePartitionAlloc = (1u << 0),
63 // Enables |FreeFlags::kSchedulerLoopQuarantine|.
64 // Requires PA-E.
65 kSchedulerLoopQuarantine = (1u << 1),
66
67 // Enables |FreeFlags::kZap|.
68 // Requires PA-E.
69 kZapOnFree = (1u << 2),
70 };
71
72 constexpr MemorySafetyCheck operator|(MemorySafetyCheck a,
73 MemorySafetyCheck b) {
74 return static_cast<MemorySafetyCheck>(static_cast<uint32_t>(a) |
75 static_cast<uint32_t>(b));
76 }
77
78 constexpr MemorySafetyCheck operator&(MemorySafetyCheck a,
79 MemorySafetyCheck b) {
80 return static_cast<MemorySafetyCheck>(static_cast<uint32_t>(a) &
81 static_cast<uint32_t>(b));
82 }
83
84 constexpr MemorySafetyCheck operator~(MemorySafetyCheck a) {
85 return static_cast<MemorySafetyCheck>(~static_cast<uint32_t>(a));
86 }
87
88 // Set of checks for ADVANCED_MEMORY_SAFETY_CHECKS() annotated objects.
89 constexpr auto kAdvancedMemorySafetyChecks =
90 MemorySafetyCheck::kForcePartitionAlloc |
91 MemorySafetyCheck::kSchedulerLoopQuarantine | MemorySafetyCheck::kZapOnFree;
92
93 // Define type traits to determine type |T|'s memory safety check status.
94 namespace {
95
96 // Allocator type traits.
ShouldUsePartitionAlloc(MemorySafetyCheck checks)97 constexpr bool ShouldUsePartitionAlloc(MemorySafetyCheck checks) {
98 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
99 return static_cast<bool>(checks &
100 (MemorySafetyCheck::kForcePartitionAlloc |
101 MemorySafetyCheck::kSchedulerLoopQuarantine |
102 MemorySafetyCheck::kZapOnFree));
103 #else
104 return false;
105 #endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
106 }
107
108 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
109
110 // Returns |partition_alloc::AllocFlags| corresponding to |checks|.
GetAllocFlags(MemorySafetyCheck checks)111 constexpr partition_alloc::AllocFlags GetAllocFlags(MemorySafetyCheck checks) {
112 return partition_alloc::AllocFlags::kReturnNull |
113 partition_alloc::AllocFlags::kNoHooks;
114 }
115
116 // Returns |partition_alloc::FreeFlags| corresponding to |checks|.
GetFreeFlags(MemorySafetyCheck checks)117 constexpr partition_alloc::FreeFlags GetFreeFlags(MemorySafetyCheck checks) {
118 auto flags = partition_alloc::FreeFlags::kNone;
119 if (static_cast<bool>(checks & MemorySafetyCheck::kSchedulerLoopQuarantine)) {
120 flags |= partition_alloc::FreeFlags::kSchedulerLoopQuarantine;
121 }
122 if (static_cast<bool>(checks & MemorySafetyCheck::kZapOnFree)) {
123 flags |= partition_alloc::FreeFlags::kZap;
124 }
125 return flags;
126 }
127
128 #endif // PA_BUILDFLAG(USE_PARTITION_ALLOC)
129
130 } // namespace
131
132 // Public utility type traits.
133 template <typename T>
134 inline constexpr MemorySafetyCheck get_memory_safety_checks = [] {
135 if constexpr (requires { T::kMemorySafetyChecks; }) {
136 return T::kMemorySafetyChecks;
137 } else {
138 return static_cast<MemorySafetyCheck>(0);
139 }
140 }();
141
142 template <typename T, MemorySafetyCheck c>
143 inline constexpr bool is_memory_safety_checked =
144 (get_memory_safety_checks<T> & c) == c;
145
146 // Allocator functions.
147 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
148 ALWAYS_INLINE partition_alloc::PartitionRoot*
GetPartitionRootForMemorySafetyCheckedAllocation()149 GetPartitionRootForMemorySafetyCheckedAllocation() {
150 return allocator_shim::internal::PartitionAllocMalloc::Allocator();
151 }
152 #endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
153
154 template <MemorySafetyCheck checks>
HandleMemorySafetyCheckedOperatorNew(std::size_t count)155 NOINLINE void* HandleMemorySafetyCheckedOperatorNew(std::size_t count) {
156 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
157 if constexpr (ShouldUsePartitionAlloc(checks)) {
158 return GetPartitionRootForMemorySafetyCheckedAllocation()
159 ->AllocInline<GetAllocFlags(checks)>(count);
160 }
161 #endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
162 return ::operator new(count);
163 }
164
165 template <MemorySafetyCheck checks>
HandleMemorySafetyCheckedOperatorNew(std::size_t count,std::align_val_t alignment)166 NOINLINE void* HandleMemorySafetyCheckedOperatorNew(
167 std::size_t count,
168 std::align_val_t alignment) {
169 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
170 if constexpr (ShouldUsePartitionAlloc(checks)) {
171 return GetPartitionRootForMemorySafetyCheckedAllocation()
172 ->AlignedAlloc<GetAllocFlags(checks)>(static_cast<size_t>(alignment),
173 count);
174 }
175 #endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
176 return ::operator new(count, alignment);
177 }
178
179 template <MemorySafetyCheck checks>
HandleMemorySafetyCheckedOperatorDelete(void * ptr)180 NOINLINE void HandleMemorySafetyCheckedOperatorDelete(void* ptr) {
181 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
182 if constexpr (ShouldUsePartitionAlloc(checks)) {
183 GetPartitionRootForMemorySafetyCheckedAllocation()
184 ->Free<GetFreeFlags(checks)>(ptr);
185 return;
186 }
187 #endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
188 ::operator delete(ptr);
189 }
190
191 template <MemorySafetyCheck checks>
HandleMemorySafetyCheckedOperatorDelete(void * ptr,std::align_val_t alignment)192 NOINLINE void HandleMemorySafetyCheckedOperatorDelete(
193 void* ptr,
194 std::align_val_t alignment) {
195 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
196 if constexpr (ShouldUsePartitionAlloc(checks)) {
197 GetPartitionRootForMemorySafetyCheckedAllocation()
198 ->Free<GetFreeFlags(checks)>(ptr);
199 return;
200 }
201 #endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
202 ::operator delete(ptr, alignment);
203 }
204
205 } // namespace base::internal
206
207 // Macros to annotate class/struct's default memory safety check.
208 // ADVANCED_MEMORY_SAFETY_CHECKS(): Enable Check |kAdvancedChecks| for this
209 // object.
210 //
211 // Note that if you use this macro at the top of struct declaration, the
212 // declaration context would be left as |private|. Please switch it back to
213 // |public| manually if needed.
214 //
215 // struct ObjectWithAdvancedChecks {
216 // ADVANCED_MEMORY_SAFETY_CHECKS();
217 // public:
218 // int public_field;
219 // };
220 #define MEMORY_SAFETY_CHECKS_INTERNAL(SPECIFIER, DEFAULT_CHECKS, \
221 ENABLED_CHECKS, DISABLED_CHECKS, ...) \
222 public: \
223 static constexpr auto kMemorySafetyChecks = [] { \
224 using enum base::internal::MemorySafetyCheck; \
225 return (DEFAULT_CHECKS | ENABLED_CHECKS) & ~(DISABLED_CHECKS); \
226 }(); \
227 SPECIFIER static void* operator new(std::size_t count) { \
228 return base::internal::HandleMemorySafetyCheckedOperatorNew< \
229 kMemorySafetyChecks>(count); \
230 } \
231 SPECIFIER static void* operator new(std::size_t count, \
232 std::align_val_t alignment) { \
233 return base::internal::HandleMemorySafetyCheckedOperatorNew< \
234 kMemorySafetyChecks>(count, alignment); \
235 } \
236 /* Though we do not hook placement new, we need to define this */ \
237 /* explicitly to allow it. */ \
238 ALWAYS_INLINE static void* operator new(std::size_t, void* ptr) { \
239 return ptr; \
240 } \
241 SPECIFIER static void operator delete(void* ptr) noexcept { \
242 base::internal::HandleMemorySafetyCheckedOperatorDelete< \
243 kMemorySafetyChecks>(ptr); \
244 } \
245 SPECIFIER static void operator delete(void* ptr, \
246 std::align_val_t alignment) noexcept { \
247 base::internal::HandleMemorySafetyCheckedOperatorDelete< \
248 kMemorySafetyChecks>(ptr, alignment); \
249 } \
250 \
251 private: \
252 static_assert(true) /* semicolon here */
253
254 #if DCHECK_IS_ON()
255 // Specify NOINLINE to display the operator on a stack trace.
256 // When 2 args provided, these two are passed to `ENABLED_CHECKS` and
257 // `DISABLED_CHECKS`. A couple of `MemorySafetyCheck::kNone` is ignored.
258 // When 1 arg provided, the one is passed to `ENABLED_CHECKS` and the first
259 // `MemorySafetyCheck::kNone` serves a default value for `DISABLED_CHECKS`.
260 // When 0 arg provided, both of `MemorySafetyCheck::kNone`s serve as default
261 // values for `ENABLED_CHECKS` and `DISABLED_CHECKS` accordingly.
262 #define ADVANCED_MEMORY_SAFETY_CHECKS(...) \
263 MEMORY_SAFETY_CHECKS_INTERNAL( \
264 NOINLINE NOT_TAIL_CALLED, \
265 base::internal::kAdvancedMemorySafetyChecks __VA_OPT__(, ) __VA_ARGS__, \
266 kNone, kNone)
267 #else
268 #define ADVANCED_MEMORY_SAFETY_CHECKS(...) \
269 MEMORY_SAFETY_CHECKS_INTERNAL( \
270 ALWAYS_INLINE, \
271 base::internal::kAdvancedMemorySafetyChecks __VA_OPT__(, ) __VA_ARGS__, \
272 kNone, kNone)
273 #endif // DCHECK_IS_ON()
274
275 // When a struct/class with `ADVANCED_MEMORY_SAFETY_CHECKS()` is inherited, a
276 // derived struct/class operator will use customized `operator new()` and
277 // `operator delete()` too. If a class has multiple base classes with the macro,
278 // a compiler may complain ambiguity between multiple `operator new()`s. On the
279 // other hand, if a class uses private inheritance, a compiler may report
280 // private `operator new()` that is making impossible to `new` that class. We
281 // have two utility macros to resolve these issues:
282 // - `INHERIT_MEMORY_SAFETY_CHECKS(BaseClass)`
283 // Explicitly exports operators from given `BaseClass` to re-apply
284 // checks specified in the parent class. This is the recommended option as
285 // a derived class is likely to have the same characteristics to its baes
286 // class. This macro accepts additional arguments to overwrite
287 // `BaseClass`'s opted-in checks.
288 // ```
289 // INHERIT_MEMORY_SAFETY_CHECKS(BaseClass,
290 // /*enable=*/ kFoo | kBar,
291 // /*disable=*/ kBaz);
292 // ```
293 // - `DEFAULT_MEMORY_SAFETY_CHECKS()`
294 // Re-define default `operator new()` and `operator delete()` using
295 // global operators that comes with default checks. This macro accepts
296 // additional arguments to enable some checks manually.
297 // ```
298 // DEFAULT_MEMORY_SAFETY_CHECKS(BaseClass,
299 // /*enable=*/ kFoo | kBar);
300 // ```
301 //
302 // Note that if you use these macros at the top of struct declaration, the
303 // declaration context would be left as |private|. Please switch it back to
304 // |public| manually if needed.
305 #define INHERIT_MEMORY_SAFETY_CHECKS(BASE_CLASS, ...) \
306 MEMORY_SAFETY_CHECKS_INTERNAL(ALWAYS_INLINE, \
307 BASE_CLASS::kMemorySafetyChecks __VA_OPT__(, ) \
308 __VA_ARGS__, \
309 kNone, kNone)
310
311 #define DEFAULT_MEMORY_SAFETY_CHECKS(...) \
312 MEMORY_SAFETY_CHECKS_INTERNAL( \
313 ALWAYS_INLINE, kNone __VA_OPT__(, ) __VA_ARGS__, kNone, kNone)
314
315 #endif // BASE_MEMORY_SAFETY_CHECKS_H_
316