#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_libc_calls
#endif
#include "base/memory/safety_checks.h"
#include "base/allocator/partition_alloc_features.h"
#include "base/feature_list.h"
#include "partition_alloc/partition_address_space.h"
#include "partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h"
#include "partition_alloc/tagging.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
using base::internal::is_memory_safety_checked;
using base::internal::MemorySafetyCheck;
struct DefaultChecks {
public:
char data[16];
};
struct AdvancedChecks {
ADVANCED_MEMORY_SAFETY_CHECKS();
public:
char data[16];
};
struct AnotherAdvancedChecks {
ADVANCED_MEMORY_SAFETY_CHECKS();
public:
char data[16];
};
constexpr int kLargeAlignment = 2 * __STDCPP_DEFAULT_NEW_ALIGNMENT__;
struct alignas(kLargeAlignment) AlignedAdvancedChecks {
ADVANCED_MEMORY_SAFETY_CHECKS();
public:
char data[16];
};
struct PrivateInheritanceWithInheritMacro : private AdvancedChecks {
INHERIT_MEMORY_SAFETY_CHECKS(AdvancedChecks);
};
static_assert(
is_memory_safety_checked<PrivateInheritanceWithInheritMacro,
MemorySafetyCheck::kForcePartitionAlloc>);
struct PrivateInheritanceWithDefaultMacro : private AdvancedChecks {
DEFAULT_MEMORY_SAFETY_CHECKS();
};
static_assert(
!is_memory_safety_checked<PrivateInheritanceWithDefaultMacro,
MemorySafetyCheck::kForcePartitionAlloc>);
struct MultipleInheritanceWithInheritMacro : AdvancedChecks,
AnotherAdvancedChecks {
INHERIT_MEMORY_SAFETY_CHECKS(AdvancedChecks);
};
static_assert(
is_memory_safety_checked<MultipleInheritanceWithInheritMacro,
MemorySafetyCheck::kForcePartitionAlloc>);
struct MultipleInheritanceWithDefaultMacro : AdvancedChecks,
AnotherAdvancedChecks {
DEFAULT_MEMORY_SAFETY_CHECKS();
};
static_assert(
!is_memory_safety_checked<MultipleInheritanceWithDefaultMacro,
MemorySafetyCheck::kForcePartitionAlloc>);
struct AdvancedChecksWithPartialOverwrite {
ADVANCED_MEMORY_SAFETY_CHECKS(kNone, kForcePartitionAlloc);
public:
char data[16];
};
static_assert(
!is_memory_safety_checked<AdvancedChecksWithPartialOverwrite,
MemorySafetyCheck::kForcePartitionAlloc>);
struct InheritanceWithPartialOverwrite : private AdvancedChecks {
INHERIT_MEMORY_SAFETY_CHECKS(AdvancedChecks, kNone, kForcePartitionAlloc);
};
static_assert(
!is_memory_safety_checked<InheritanceWithPartialOverwrite,
MemorySafetyCheck::kForcePartitionAlloc>);
TEST(MemorySafetyCheckTest, AllocatorFunctions) {
static_assert(
!is_memory_safety_checked<DefaultChecks,
MemorySafetyCheck::kForcePartitionAlloc>);
static_assert(
is_memory_safety_checked<AdvancedChecks,
MemorySafetyCheck::kForcePartitionAlloc>);
static_assert(
is_memory_safety_checked<AlignedAdvancedChecks,
MemorySafetyCheck::kForcePartitionAlloc>);
auto* ptr1 = new DefaultChecks();
auto* ptr2 = new AdvancedChecks();
EXPECT_NE(ptr1, nullptr);
EXPECT_NE(ptr2, nullptr);
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
reinterpret_cast<uintptr_t>(partition_alloc::UntagPtr(ptr2))));
#endif
delete ptr1;
delete ptr2;
ptr1 = new (std::align_val_t(64)) DefaultChecks();
ptr2 = new (std::align_val_t(64)) AdvancedChecks();
EXPECT_NE(ptr1, nullptr);
EXPECT_NE(ptr2, nullptr);
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
reinterpret_cast<uintptr_t>(partition_alloc::UntagPtr(ptr2))));
#endif
::operator delete(ptr1, std::align_val_t(64));
AdvancedChecks::operator delete(ptr2, std::align_val_t(64));
auto* ptr3 = new AlignedAdvancedChecks();
EXPECT_NE(ptr3, nullptr);
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
reinterpret_cast<uintptr_t>(partition_alloc::UntagPtr(ptr3))));
#endif
delete ptr3;
alignas(AlignedAdvancedChecks) char data[32];
ptr1 = new (data) DefaultChecks();
ptr2 = new (data) AdvancedChecks();
ptr3 = new (data) AlignedAdvancedChecks();
}
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
TEST(MemorySafetyCheckTest, SchedulerLoopQuarantine) {
if (!base::FeatureList::IsEnabled(
base::features::kPartitionAllocSchedulerLoopQuarantine)) {
return;
}
static_assert(
!is_memory_safety_checked<DefaultChecks,
MemorySafetyCheck::kSchedulerLoopQuarantine>);
static_assert(
is_memory_safety_checked<AdvancedChecks,
MemorySafetyCheck::kSchedulerLoopQuarantine>);
auto* root = allocator_shim::internal::PartitionAllocMalloc::Allocator();
partition_alloc::internal::
ScopedSchedulerLoopQuarantineBranchAccessorForTesting branch(root);
auto* ptr1 = new DefaultChecks();
ASSERT_NE(ptr1, nullptr);
delete ptr1;
EXPECT_FALSE(branch.IsQuarantined(ptr1));
auto* ptr2 = new AdvancedChecks();
ASSERT_NE(ptr2, nullptr);
memset(ptr2->data, 'A', sizeof(ptr2->data));
delete ptr2;
EXPECT_TRUE(branch.IsQuarantined(ptr2));
EXPECT_NE(ptr2->data[0], 'A');
EXPECT_NE(ptr2->data[15], 'A');
branch.Purge();
}
#endif
}