• 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_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