blob: de69a5fa6ec5dc2de6d2ced0bee0527be649bb85 [file]
// 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.
#include "base/memory/sanitized_object.h"
#include "partition_alloc/buildflags.h"
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
#include "partition_alloc/partition_alloc_public_constants.h" // nogncheck
#include "partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h"
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
// 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 {
// Define type traits to determine type |T|'s memory safety check status.
namespace {
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
// Allocator type traits.
constexpr bool ShouldUsePartitionAlloc(MemorySafetyCheck checks) {
return static_cast<bool>(checks &
(MemorySafetyCheck::kForcePartitionAlloc |
MemorySafetyCheck::kSchedulerLoopQuarantine));
}
constexpr bool IsLeakedSanitizedObject(MemorySafetyCheck checks) {
constexpr MemorySafetyCheck flags = MemorySafetyCheck::kForcePartitionAlloc |
MemorySafetyCheck::kInfiniteQuarantine;
return (checks & flags) == flags;
}
// Returns |partition_alloc::AllocFlags| corresponding to |checks|.
constexpr partition_alloc::AllocFlags GetAllocFlags(MemorySafetyCheck checks) {
return partition_alloc::AllocFlags::kReturnNull |
partition_alloc::AllocFlags::kNoHooks;
}
// Returns |partition_alloc::FreeFlags| corresponding to |checks|.
constexpr partition_alloc::FreeFlags GetFreeFlags(MemorySafetyCheck checks) {
auto flags = partition_alloc::FreeFlags::kNone;
if (static_cast<bool>(checks & MemorySafetyCheck::kSchedulerLoopQuarantine)) {
flags |=
partition_alloc::FreeFlags::kSchedulerLoopQuarantineForSanitizedObject;
}
if (static_cast<bool>(checks & MemorySafetyCheck::kInfiniteQuarantine)) {
flags |= partition_alloc::FreeFlags::kIntendedLeak;
}
return flags;
}
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
} // namespace
// Allocator functions.
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
ALWAYS_INLINE partition_alloc::PartitionRoot*
GetPartitionRootForMemorySafetyCheckedAllocation() {
return allocator_shim::internal::PartitionAllocMalloc::Allocator();
}
ALWAYS_INLINE partition_alloc::PartitionRoot*
GetPartitionRootForLeakedSecurityObjectAllocation() {
return allocator_shim::internal::PartitionAllocMalloc::
IntendedLeakAllocator();
}
// For malloc_dump_provider and tests, use this NOINLINE function.
// The function is a just wrapper of inline
// GetPartitionRootForLeakedSecurityObjectAllocation().
NOINLINE partition_alloc::PartitionRoot* LeakedSecurityObjectAllocator() {
return GetPartitionRootForLeakedSecurityObjectAllocation();
}
#else // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
NOINLINE partition_alloc::PartitionRoot* LeakedSecurityObjectAllocator() {
return nullptr;
}
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
template <MemorySafetyCheck checks>
NOINLINE void* HandleMemorySafetyCheckedOperatorNew(std::size_t count) {
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
if constexpr (IsLeakedSanitizedObject(checks)) {
return GetPartitionRootForLeakedSecurityObjectAllocation()
->Alloc<GetAllocFlags(checks)>(count);
}
if constexpr (ShouldUsePartitionAlloc(checks)) {
return GetPartitionRootForMemorySafetyCheckedAllocation()
->Alloc<GetAllocFlags(checks)>(count);
}
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
return ::operator new(count);
}
template <MemorySafetyCheck checks>
NOINLINE void* HandleMemorySafetyCheckedOperatorNew(
std::size_t count,
std::align_val_t alignment) {
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
if constexpr (IsLeakedSanitizedObject(checks)) {
return GetPartitionRootForLeakedSecurityObjectAllocation()
->AlignedAlloc<GetAllocFlags(checks)>(static_cast<size_t>(alignment),
count);
}
if constexpr (ShouldUsePartitionAlloc(checks)) {
return GetPartitionRootForMemorySafetyCheckedAllocation()
->AlignedAlloc<GetAllocFlags(checks)>(static_cast<size_t>(alignment),
count);
}
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
return ::operator new(count, alignment);
}
template <MemorySafetyCheck checks>
NOINLINE void HandleMemorySafetyCheckedOperatorDelete(
void* ptr,
[[maybe_unused]] uint32_t type_id) {
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
if constexpr (IsLeakedSanitizedObject(checks)) {
return GetPartitionRootForLeakedSecurityObjectAllocation()
->Free<GetFreeFlags(checks)>(ptr,
partition_alloc::FreeTypeIdHint{type_id});
}
if constexpr (ShouldUsePartitionAlloc(checks)) {
GetPartitionRootForMemorySafetyCheckedAllocation()
->Free<GetFreeFlags(checks)>(ptr);
return;
}
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
::operator delete(ptr);
}
template <MemorySafetyCheck checks>
NOINLINE void HandleMemorySafetyCheckedOperatorDelete(
void* ptr,
std::align_val_t alignment,
[[maybe_unused]] uint32_t type_id) {
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
if constexpr (IsLeakedSanitizedObject(checks)) {
return GetPartitionRootForLeakedSecurityObjectAllocation()
->Free<GetFreeFlags(checks)>(ptr,
partition_alloc::FreeTypeIdHint{type_id});
}
if constexpr (ShouldUsePartitionAlloc(checks)) {
GetPartitionRootForMemorySafetyCheckedAllocation()
->Free<GetFreeFlags(checks)>(ptr);
return;
}
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
::operator delete(ptr, alignment);
}
FOR_EACH_BASE_INTERNAL_MEMORY_SAFETY_CHECK_VALUE(
DEFINE_BASE_INTERNAL_HANDLE_MEMORY_SAFETY_CHECKED_OPERATORS)
uintptr_t GetPartitionRootForLeakedSecurityObjectAllocationForTesting() {
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
return reinterpret_cast<uintptr_t>(
GetPartitionRootForLeakedSecurityObjectAllocation());
#else
return 0;
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
}
} // namespace base::internal