#include <cstdint>
#include <string>
#include <vector>
#include "base/allocator/partition_allocator/partition_alloc_config.h"
#include "base/allocator/partition_allocator/partition_freelist_entry.h"
#include "base/allocator/partition_allocator/partition_page.h"
#include "base/allocator/partition_allocator/partition_root.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
namespace partition_alloc::internal {
namespace {
#if !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) && \
PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY)
TEST(HardeningTest, PartialCorruption) {
std::string important_data("very important");
char* to_corrupt = const_cast<char*>(important_data.c_str());
PartitionRoot<ThreadSafe> root({
PartitionOptions::AlignedAlloc::kAllowed,
PartitionOptions::ThreadCache::kDisabled,
PartitionOptions::Quarantine::kDisallowed,
PartitionOptions::Cookie::kDisallowed,
PartitionOptions::BackupRefPtr::kDisabled,
PartitionOptions::BackupRefPtrZapping::kDisabled,
PartitionOptions::UseConfigurablePool::kNo,
});
root.UncapEmptySlotSpanMemoryForTesting();
const size_t kAllocSize = 100;
void* data = root.Alloc(kAllocSize, "");
void* data2 = root.Alloc(kAllocSize, "");
root.Free(data2);
root.Free(data);
PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
to_corrupt, false);
EXPECT_DEATH(root.Alloc(kAllocSize, ""), "");
}
TEST(HardeningTest, OffHeapPointerCrashing) {
std::string important_data("very important");
char* to_corrupt = const_cast<char*>(important_data.c_str());
PartitionRoot<ThreadSafe> root({
PartitionOptions::AlignedAlloc::kAllowed,
PartitionOptions::ThreadCache::kDisabled,
PartitionOptions::Quarantine::kDisallowed,
PartitionOptions::Cookie::kDisallowed,
PartitionOptions::BackupRefPtr::kDisabled,
PartitionOptions::BackupRefPtrZapping::kDisabled,
PartitionOptions::UseConfigurablePool::kNo,
});
root.UncapEmptySlotSpanMemoryForTesting();
const size_t kAllocSize = 100;
void* data = root.Alloc(kAllocSize, "");
void* data2 = root.Alloc(kAllocSize, "");
root.Free(data2);
root.Free(data);
PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
to_corrupt, true);
EXPECT_DEATH(root.Alloc(kAllocSize, ""), "");
}
TEST(HardeningTest, MetadataPointerCrashing) {
PartitionRoot<ThreadSafe> root({
PartitionOptions::AlignedAlloc::kAllowed,
PartitionOptions::ThreadCache::kDisabled,
PartitionOptions::Quarantine::kDisallowed,
PartitionOptions::Cookie::kDisallowed,
PartitionOptions::BackupRefPtr::kDisabled,
PartitionOptions::BackupRefPtrZapping::kDisabled,
PartitionOptions::UseConfigurablePool::kNo,
});
root.UncapEmptySlotSpanMemoryForTesting();
const size_t kAllocSize = 100;
void* data = root.Alloc(kAllocSize, "");
void* data2 = root.Alloc(kAllocSize, "");
root.Free(data2);
root.Free(data);
uintptr_t slot_start = root.ObjectToSlotStart(data);
auto* metadata = SlotSpanMetadata<ThreadSafe>::FromSlotStart(slot_start);
PartitionFreelistEntry::EmplaceAndInitForTest(slot_start, metadata, true);
EXPECT_DEATH(root.Alloc(kAllocSize, ""), "");
}
#endif
#if !BUILDFLAG(IS_ANDROID)
TEST(HardeningTest, SuccessfulCorruption) {
PartitionRoot<ThreadSafe> root({
PartitionOptions::AlignedAlloc::kAllowed,
PartitionOptions::ThreadCache::kDisabled,
PartitionOptions::Quarantine::kDisallowed,
PartitionOptions::Cookie::kDisallowed,
PartitionOptions::BackupRefPtr::kDisabled,
PartitionOptions::BackupRefPtrZapping::kDisabled,
PartitionOptions::UseConfigurablePool::kNo,
});
root.UncapEmptySlotSpanMemoryForTesting();
uintptr_t* zero_vector = reinterpret_cast<uintptr_t*>(
root.AllocWithFlags(AllocFlags::kZeroFill, 100 * sizeof(uintptr_t), ""));
ASSERT_TRUE(zero_vector);
uintptr_t* to_corrupt = zero_vector + 20;
const size_t kAllocSize = 100;
void* data = root.Alloc(kAllocSize, "");
void* data2 = root.Alloc(kAllocSize, "");
root.Free(data2);
root.Free(data);
PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
to_corrupt, true);
#if BUILDFLAG(USE_FREESLOT_BITMAP)
EXPECT_DEATH_IF_SUPPORTED(root.Alloc(kAllocSize, ""), "");
#else
void* new_data = root.Alloc(kAllocSize, "");
ASSERT_EQ(new_data, data);
void* new_data2 = root.Alloc(kAllocSize, "");
EXPECT_EQ(new_data2, to_corrupt);
#endif
}
#endif
}
}
#endif