| // Copyright 2023 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef BASE_MEMORY_SANITIZED_OBJECT_H_ | |
| #define BASE_MEMORY_SANITIZED_OBJECT_H_ | |
| // This header provides `SANITIZED_OBJECT` macro, which may be used | |
| // in any class that needs lifetime investigations. Hence, this header may be | |
| // included in many translation units, and the build time may be impacted by the | |
| // total size of this header including the size of the headers directly or | |
| // indirectly included from this header. Thus, the header dependency is | |
| // minimized. It's especially avoided to include PartitionAlloc's large headers. | |
| #include <cstdint> | |
| #include <new> | |
| #include "base/base_export.h" | |
| #include "base/compiler_specific.h" | |
| #include "base/dcheck_is_on.h" | |
| #include "base/export_template.h" | |
| #include "partition_alloc/fnv1a_consteval.h" // nogncheck | |
| // This header defines `SANITIZED_OBJECT()` macro. | |
| // They can be used to specify a class/struct that is targeted to perform | |
| // additional CHECKS across variety of memory safety mechanisms such as | |
| // PartitionAllocator. | |
| // ``` | |
| // class Foo { | |
| // SANITIZED_OBJECT(); | |
| // } | |
| // ``` | |
| // Checks here are disabled by default because of their performance cost. | |
| // Currently, the macro is managed by the memory safety team internally and | |
| // you should not add / remove it manually. | |
| // | |
| // Additional checks here are categorized into either one of enum | |
| // `MemorySafetyCheck`. Some of them are too costly and disabled even for | |
| // `SANITIZED_OBJECT()` annotated types. These checks can be | |
| // enabled by passing optional arguments to the macro. | |
| // ``` | |
| // class Foo { | |
| // SANITIZED_OBJECT( | |
| // /*enable=*/ kFoo | kBar); | |
| // } | |
| // ``` | |
| // It is also possible to disable default checks for annotated types. | |
| // ``` | |
| // class Foo { | |
| // SANITIZED_OBJECT( | |
| // /*enable=*/ kFoo, | |
| // /*disable=*/ kBaz); | |
| // } | |
| // ``` | |
| // Macros to annotate class/struct's default memory safety check. | |
| // SANITIZED_OBJECT(): Enable Check |kAdvancedChecks| for this | |
| // object. | |
| // | |
| // Note that if you use this macro at the top of struct declaration, the | |
| // declaration context would be left as |private|. Please switch it back to | |
| // |public| manually if needed. | |
| // | |
| // struct ObjectWithAdvancedChecks { | |
| // SANITIZED_OBJECT(); | |
| // public: | |
| // int public_field; | |
| // }; | |
| #define MEMORY_SAFETY_CHECKS_INTERNAL(SPECIFIER, DEFAULT_CHECKS, \ | |
| ENABLED_CHECKS, DISABLED_CHECKS, ...) \ | |
| public: \ | |
| static constexpr auto kMemorySafetyChecks = [] { \ | |
| using enum base::internal::MemorySafetyCheck; \ | |
| return (DEFAULT_CHECKS | ENABLED_CHECKS) & ~(DISABLED_CHECKS); \ | |
| }(); \ | |
| SPECIFIER static void* operator new(std::size_t count) { \ | |
| return base::internal::HandleMemorySafetyCheckedOperatorNew< \ | |
| kMemorySafetyChecks>(count); \ | |
| } \ | |
| SPECIFIER static void* operator new(std::size_t count, \ | |
| std::align_val_t alignment) { \ | |
| return base::internal::HandleMemorySafetyCheckedOperatorNew< \ | |
| kMemorySafetyChecks>(count, alignment); \ | |
| } \ | |
| /* Though we do not hook placement new, we need to define this */ \ | |
| /* explicitly to allow it. */ \ | |
| ALWAYS_INLINE static void* operator new(std::size_t, void* ptr) { \ | |
| return ptr; \ | |
| } \ | |
| SPECIFIER static void operator delete(void* ptr) noexcept { \ | |
| base::internal::HandleMemorySafetyCheckedOperatorDelete< \ | |
| kMemorySafetyChecks>(ptr, kPartitionAllocSanitizedObjectTypeId); \ | |
| } \ | |
| SPECIFIER static void operator delete(void* ptr, \ | |
| std::align_val_t alignment) noexcept { \ | |
| base::internal::HandleMemorySafetyCheckedOperatorDelete< \ | |
| kMemorySafetyChecks>(ptr, alignment, \ | |
| kPartitionAllocSanitizedObjectTypeId); \ | |
| } \ | |
| \ | |
| private: \ | |
| static constexpr uint32_t kPartitionAllocSanitizedObjectTypeId = [] { \ | |
| return partition_alloc::fnv1a_hash(std::string_view(__PRETTY_FUNCTION__)); \ | |
| }(); \ | |
| \ | |
| static_assert(true) /* semicolon here */ | |
| #if DCHECK_IS_ON() | |
| // Specify NOINLINE to display the operator on a stack trace. | |
| // When 2 args provided, these two are passed to `ENABLED_CHECKS` and | |
| // `DISABLED_CHECKS`. A couple of `MemorySafetyCheck::kNone` is ignored. | |
| // When 1 arg provided, the one is passed to `ENABLED_CHECKS` and the first | |
| // `MemorySafetyCheck::kNone` serves a default value for `DISABLED_CHECKS`. | |
| // When 0 arg provided, both of `MemorySafetyCheck::kNone`s serve as default | |
| // values for `ENABLED_CHECKS` and `DISABLED_CHECKS` accordingly. | |
| #define SANITIZED_OBJECT(...) \ | |
| MEMORY_SAFETY_CHECKS_INTERNAL( \ | |
| NOINLINE NOT_TAIL_CALLED, \ | |
| base::internal::kSanitizedObject __VA_OPT__(, ) __VA_ARGS__, kNone, \ | |
| kNone) | |
| #define LEAKED_SANITIZED_OBJECT(...) \ | |
| MEMORY_SAFETY_CHECKS_INTERNAL( \ | |
| NOINLINE NOT_TAIL_CALLED, \ | |
| base::internal::kLeakedSanitizedObject __VA_OPT__(, ) __VA_ARGS__, \ | |
| kNone, kNone) | |
| #else | |
| #define SANITIZED_OBJECT(...) \ | |
| MEMORY_SAFETY_CHECKS_INTERNAL( \ | |
| ALWAYS_INLINE, \ | |
| base::internal::kSanitizedObject __VA_OPT__(, ) __VA_ARGS__, kNone, \ | |
| kNone) | |
| #define LEAKED_SANITIZED_OBJECT(...) \ | |
| MEMORY_SAFETY_CHECKS_INTERNAL( \ | |
| ALWAYS_INLINE, \ | |
| base::internal::kLeakedSanitizedObject __VA_OPT__(, ) __VA_ARGS__, \ | |
| kNone, kNone) | |
| #endif // DCHECK_IS_ON() | |
| // When a struct/class with `SANITIZED_OBJECT()` is inherited, a | |
| // derived struct/class operator will use customized `operator new()` and | |
| // `operator delete()` too. If a class has multiple base classes with the macro, | |
| // a compiler may complain ambiguity between multiple `operator new()`s. On the | |
| // other hand, if a class uses private inheritance, a compiler may report | |
| // private `operator new()` that is making impossible to `new` that class. We | |
| // have two utility macros to resolve these issues: | |
| // - `INHERIT_SANITIZED_OBJECT(BaseClass)` | |
| // Explicitly exports operators from given `BaseClass` to re-apply | |
| // checks specified in the parent class. This is the recommended option as | |
| // a derived class is likely to have the same characteristics to its baes | |
| // class. This macro accepts additional arguments to overwrite | |
| // `BaseClass`'s opted-in checks. | |
| // ``` | |
| // INHERIT_SANITIZED_OBJECT(BaseClass, | |
| // /*enable=*/ kFoo | kBar, | |
| // /*disable=*/ kBaz); | |
| // ``` | |
| // - `NO_SANITIZED_OBJECT()` | |
| // Re-define default `operator new()` and `operator delete()` using | |
| // global operators that comes with default checks. This macro accepts | |
| // additional arguments to enable some checks manually. | |
| // ``` | |
| // NO_SANITIZED_OBJECT(BaseClass, | |
| // /*enable=*/ kFoo | kBar); | |
| // ``` | |
| // | |
| // Note that if you use these macros at the top of struct declaration, the | |
| // declaration context would be left as |private|. Please switch it back to | |
| // |public| manually if needed. | |
| #define INHERIT_SANITIZED_OBJECT(BASE_CLASS, ...) \ | |
| MEMORY_SAFETY_CHECKS_INTERNAL(ALWAYS_INLINE, \ | |
| BASE_CLASS::kMemorySafetyChecks __VA_OPT__(, ) \ | |
| __VA_ARGS__, \ | |
| kNone, kNone) | |
| #define NO_SANITIZED_OBJECT(...) \ | |
| MEMORY_SAFETY_CHECKS_INTERNAL( \ | |
| ALWAYS_INLINE, kNone __VA_OPT__(, ) __VA_ARGS__, kNone, kNone) | |
| namespace partition_alloc { | |
| class PartitionRoot; | |
| } // namespace partition_alloc | |
| // We cannot hide things behind anonymous namespace because they are referenced | |
| // via macro, which can be defined anywhere. | |
| // To avoid tainting ::base namespace, define things inside this namespace. | |
| namespace base::internal { | |
| enum class MemorySafetyCheck : uint32_t { | |
| kNone = 0, | |
| kForcePartitionAlloc = (1u << 0), | |
| // Enables |FreeFlags::kSchedulerLoopQuarantineForSanitizedObject|. | |
| // Requires PA-E. | |
| kSchedulerLoopQuarantine = (1u << 1), | |
| // Enables |FreeFlags::kInfiniteQuarantine|. Requires PA-E. | |
| kInfiniteQuarantine = 1u << 2, | |
| }; | |
| #define FOR_EACH_BASE_INTERNAL_MEMORY_SAFETY_CHECK_VALUE(MACRO) \ | |
| MACRO(::base::internal::MemorySafetyCheck::kNone) \ | |
| MACRO(::base::internal::MemorySafetyCheck::kForcePartitionAlloc) \ | |
| MACRO(::base::internal::MemorySafetyCheck::kSchedulerLoopQuarantine) \ | |
| MACRO(::base::internal::MemorySafetyCheck::kForcePartitionAlloc | \ | |
| ::base::internal::MemorySafetyCheck::kSchedulerLoopQuarantine) \ | |
| MACRO(::base::internal::MemorySafetyCheck::kInfiniteQuarantine) \ | |
| MACRO(::base::internal::MemorySafetyCheck::kForcePartitionAlloc | \ | |
| ::base::internal::MemorySafetyCheck::kInfiniteQuarantine) | |
| constexpr MemorySafetyCheck operator|(MemorySafetyCheck a, | |
| MemorySafetyCheck b) { | |
| return static_cast<MemorySafetyCheck>(static_cast<uint32_t>(a) | | |
| static_cast<uint32_t>(b)); | |
| } | |
| constexpr MemorySafetyCheck operator&(MemorySafetyCheck a, | |
| MemorySafetyCheck b) { | |
| return static_cast<MemorySafetyCheck>(static_cast<uint32_t>(a) & | |
| static_cast<uint32_t>(b)); | |
| } | |
| constexpr MemorySafetyCheck operator~(MemorySafetyCheck a) { | |
| return static_cast<MemorySafetyCheck>(~static_cast<uint32_t>(a)); | |
| } | |
| // Utility type traits. | |
| template <typename T> | |
| inline constexpr MemorySafetyCheck get_memory_safety_checks = [] { | |
| if constexpr (requires { T::kMemorySafetyChecks; }) { | |
| return T::kMemorySafetyChecks; | |
| } else { | |
| return static_cast<MemorySafetyCheck>(0); | |
| } | |
| }(); | |
| template <typename T, MemorySafetyCheck c> | |
| inline constexpr bool is_memory_safety_checked = | |
| (get_memory_safety_checks<T> & c) == c; | |
| // Set of checks for SANITIZED_OBJECT() annotated objects. | |
| inline constexpr auto kSanitizedObject = | |
| MemorySafetyCheck::kForcePartitionAlloc | | |
| MemorySafetyCheck::kSchedulerLoopQuarantine; | |
| inline constexpr auto kLeakedSanitizedObject = | |
| MemorySafetyCheck::kForcePartitionAlloc | | |
| MemorySafetyCheck::kInfiniteQuarantine; | |
| template <MemorySafetyCheck checks> | |
| void* HandleMemorySafetyCheckedOperatorNew(std::size_t count); | |
| template <MemorySafetyCheck checks> | |
| void* HandleMemorySafetyCheckedOperatorNew(std::size_t count, | |
| std::align_val_t alignment); | |
| template <MemorySafetyCheck checks> | |
| void HandleMemorySafetyCheckedOperatorDelete(void* ptr, uint32_t type_id); | |
| template <MemorySafetyCheck checks> | |
| void HandleMemorySafetyCheckedOperatorDelete(void* ptr, | |
| std::align_val_t alignment, | |
| uint32_t type_id); | |
| #define INSTANTIATE_HANDLE_MEMORY_SAFETY_CHECKED_OPERATORS(EXPORT, checks) \ | |
| EXPORT void* HandleMemorySafetyCheckedOperatorNew<checks>( \ | |
| std::size_t count); \ | |
| EXPORT void* HandleMemorySafetyCheckedOperatorNew<checks>( \ | |
| std::size_t count, std::align_val_t alignment); \ | |
| EXPORT void HandleMemorySafetyCheckedOperatorDelete<checks>( \ | |
| void* ptr, uint32_t type_id); \ | |
| EXPORT void HandleMemorySafetyCheckedOperatorDelete<checks>( \ | |
| void* ptr, std::align_val_t alignment, uint32_t type_id); | |
| #define DECLARE_BASE_INTERNAL_HANDLE_MEMORY_SAFETY_CHECKED_OPERATORS(checks) \ | |
| INSTANTIATE_HANDLE_MEMORY_SAFETY_CHECKED_OPERATORS( \ | |
| extern template EXPORT_TEMPLATE_DECLARE(BASE_EXPORT), checks) | |
| #define DEFINE_BASE_INTERNAL_HANDLE_MEMORY_SAFETY_CHECKED_OPERATORS(checks) \ | |
| INSTANTIATE_HANDLE_MEMORY_SAFETY_CHECKED_OPERATORS( \ | |
| template EXPORT_TEMPLATE_DEFINE(BASE_EXPORT), checks) | |
| FOR_EACH_BASE_INTERNAL_MEMORY_SAFETY_CHECK_VALUE( | |
| DECLARE_BASE_INTERNAL_HANDLE_MEMORY_SAFETY_CHECKED_OPERATORS) | |
| BASE_EXPORT partition_alloc::PartitionRoot* LeakedSecurityObjectAllocator(); | |
| } // namespace base::internal | |
| #endif // BASE_MEMORY_SANITIZED_OBJECT_H_ |