#include "base/apple/scoped_mach_vm.h"
#include <mach/mach.h>
#include "base/memory/page_size.h"
#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base::apple {
namespace {
constexpr int kVmAllocateTagNumber = 248;
static_assert(kVmAllocateTagNumber >= VM_MEMORY_APPLICATION_SPECIFIC_1);
static_assert(kVmAllocateTagNumber <= VM_MEMORY_APPLICATION_SPECIFIC_16);
constexpr int kVmAllocateTag = VM_MAKE_TAG(kVmAllocateTagNumber);
bool GetRegionInfo(vm_address_t* region_address, vm_size_t* region_size) {
vm_region_basic_info_64 region_info;
mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
mach_port_t object;
kern_return_t kr = vm_region_64(
mach_task_self(), region_address, region_size, VM_REGION_BASIC_INFO_64,
reinterpret_cast<vm_region_info_t>(®ion_info), &count, &object);
EXPECT_EQ(kr, KERN_SUCCESS);
return kr == KERN_SUCCESS;
}
TEST(ScopedMachVMTest, Basic) {
const vm_size_t kOnePage = base::GetPageSize();
vm_address_t address;
kern_return_t kr = vm_allocate(mach_task_self(), &address, kOnePage,
VM_FLAGS_ANYWHERE | kVmAllocateTag);
ASSERT_EQ(kr, KERN_SUCCESS);
ScopedMachVM scoper(address, kOnePage);
EXPECT_EQ(scoper.address(), address);
EXPECT_EQ(scoper.size(), kOnePage);
vm_address_t region_address = address;
vm_size_t region_size;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kOnePage);
}
{
ScopedMachVM scoper2;
EXPECT_EQ(scoper2.address(), 0u);
EXPECT_EQ(scoper2.size(), 0u);
scoper.swap(scoper2);
EXPECT_EQ(scoper2.address(), address);
EXPECT_EQ(scoper2.size(), kOnePage);
EXPECT_EQ(scoper.address(), 0u);
EXPECT_EQ(scoper.size(), 0u);
}
region_address = address;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_GE(region_address, address + kOnePage);
}
}
TEST(ScopedMachVMTest, Reset) {
const vm_size_t kOnePage = base::GetPageSize();
vm_address_t address;
kern_return_t kr = vm_allocate(mach_task_self(), &address, kOnePage,
VM_FLAGS_ANYWHERE | kVmAllocateTag);
ASSERT_EQ(kr, KERN_SUCCESS);
ScopedMachVM scoper(address, kOnePage);
vm_address_t region_address = address;
vm_size_t region_size;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kOnePage);
}
scoper.reset();
region_address = address;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_GE(region_address, address + kOnePage);
}
}
TEST(ScopedMachVMTest, ResetSmallerAddress) {
const vm_size_t kOnePage = base::GetPageSize();
const vm_size_t kThreePages = 3 * kOnePage;
vm_address_t address;
kern_return_t kr = vm_allocate(mach_task_self(), &address, kThreePages,
VM_FLAGS_ANYWHERE | kVmAllocateTag);
ASSERT_EQ(kr, KERN_SUCCESS);
ScopedMachVM scoper(address, kThreePages);
vm_address_t region_address = address;
vm_size_t region_size;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kThreePages);
}
scoper.reset(address + kOnePage, kOnePage);
region_address = address;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address + kOnePage);
EXPECT_EQ(region_size, kOnePage);
}
}
TEST(ScopedMachVMTest, ResetLargerAddressAndSize) {
const vm_size_t kOnePage = base::GetPageSize();
const vm_size_t kTwoPages = 2 * kOnePage;
const vm_size_t kThreePages = 3 * kOnePage;
vm_address_t address;
kern_return_t kr = vm_allocate(mach_task_self(), &address, kThreePages,
VM_FLAGS_ANYWHERE | kVmAllocateTag);
ASSERT_EQ(kr, KERN_SUCCESS);
vm_address_t region_address = address;
vm_size_t region_size;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kThreePages);
}
ScopedMachVM scoper(address + kTwoPages, kOnePage);
scoper.reset(address, kThreePages);
region_address = address;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kThreePages);
}
}
TEST(ScopedMachVMTest, ResetLargerAddress) {
const vm_size_t kOnePage = base::GetPageSize();
const vm_size_t kThreePages = 3 * kOnePage;
const vm_size_t kSixPages = 6 * kOnePage;
vm_address_t address;
kern_return_t kr = vm_allocate(mach_task_self(), &address, kSixPages,
VM_FLAGS_ANYWHERE | kVmAllocateTag);
ASSERT_EQ(kr, KERN_SUCCESS);
vm_address_t region_address = address;
vm_size_t region_size;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kSixPages);
}
ScopedMachVM scoper(address + kThreePages, kThreePages);
scoper.reset(address, kThreePages);
region_address = address;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kThreePages);
}
}
TEST(ScopedMachVMTest, ResetUnaligned) {
const vm_size_t kOnePage = base::GetPageSize();
const vm_size_t kTwoPages = 2 * kOnePage;
vm_address_t address;
kern_return_t kr = vm_allocate(mach_task_self(), &address, kTwoPages,
VM_FLAGS_ANYWHERE | kVmAllocateTag);
ASSERT_EQ(kr, KERN_SUCCESS);
ScopedMachVM scoper;
vm_address_t region_address = address;
vm_size_t region_size;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kTwoPages);
}
scoper.reset_unaligned(address + kOnePage, kOnePage - 3);
scoper.reset_unaligned(address + kOnePage, kOnePage - 11);
region_address = address;
if (GetRegionInfo(®ion_address, ®ion_size)) {
EXPECT_EQ(region_address, address);
EXPECT_EQ(region_size, kOnePage);
}
scoper.reset_unaligned(address, kOnePage);
}
#if DCHECK_IS_ON()
TEST(ScopedMachVMTest, ResetMustBeAligned) {
const vm_size_t kOnePage = base::GetPageSize();
const vm_size_t kTwoPages = 2 * kOnePage;
vm_address_t address;
kern_return_t kr = vm_allocate(mach_task_self(), &address, kTwoPages,
VM_FLAGS_ANYWHERE | kVmAllocateTag);
ASSERT_EQ(kr, KERN_SUCCESS);
ScopedMachVM scoper;
EXPECT_DCHECK_DEATH(scoper.reset(address, kOnePage + 1));
}
#endif
}
}