#include "gin/v8_platform_page_allocator.h"
#include "base/check_op.h"
#include "base/cpu.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(ARCH_CPU_ARM64) && (OS_LINUX || OS_ANDROID)
#include "base/allocator/partition_allocator/arm_bti_test_functions.h"
#include "base/allocator/partition_allocator/page_allocator_constants.h"
#if BUILDFLAG(IS_POSIX)
#include <signal.h>
#include "testing/gtest/include/gtest/gtest-death-test.h"
#endif
#endif
namespace gin {
TEST(V8PlatformPageAllocatorTest, VerifyGetPageConfig) {
auto sut = gin::PageAllocator();
CHECK_EQ(sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kNoAccess),
partition_alloc::PageAccessibilityConfiguration::kInaccessible);
CHECK_EQ(sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kRead),
partition_alloc::PageAccessibilityConfiguration::kRead);
CHECK_EQ(
sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kReadWrite),
partition_alloc::PageAccessibilityConfiguration::kReadWrite);
CHECK_EQ(sut.GetPageConfigPermissionsForTesting(
v8::PageAllocator::kReadWriteExecute),
partition_alloc::PageAccessibilityConfiguration::kReadWriteExecute);
#if defined(__ARM_FEATURE_BTI_DEFAULT)
CHECK_EQ(
sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kReadExecute),
base::CPU::GetInstanceNoAllocation().has_bti()
? partition_alloc::PageAccessibilityConfiguration::
kReadExecuteProtected
: partition_alloc::PageAccessibilityConfiguration::kReadExecute);
#else
CHECK_EQ(
sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kReadExecute),
partition_alloc::PageAccessibilityConfiguration::kReadExecute);
#endif
CHECK_EQ(sut.GetPageConfigPermissionsForTesting(
v8::PageAllocator::kNoAccessWillJitLater),
partition_alloc::PageAccessibilityConfiguration::kInaccessible);
}
#if defined(ARCH_CPU_ARM64) && (OS_LINUX || OS_ANDROID)
using BTITestFunction = int64_t (*)(int64_t);
TEST(V8PlatformPageAllocatorBTITest, VerifyReadExecutePagesAreProtected) {
auto page_allocator = gin::PageAllocator();
auto const memory_size =
partition_alloc::internal::PageAllocationGranularity();
auto const memory_alignment =
partition_alloc::internal::PageAllocationGranularity();
char* const buffer = reinterpret_cast<char*>(page_allocator.AllocatePages(
nullptr, memory_size, memory_alignment,
v8::PageAllocator::Permission::kReadWriteExecute));
ptrdiff_t const function_range =
reinterpret_cast<char*>(arm_bti_test_function_end) -
reinterpret_cast<char*>(arm_bti_test_function);
ptrdiff_t const invalid_offset =
reinterpret_cast<char*>(arm_bti_test_function_invalid_offset) -
reinterpret_cast<char*>(arm_bti_test_function);
EXPECT_EQ(0u, ((uint64_t)buffer) % 4);
EXPECT_EQ(0u, ((uint64_t)function_range) % 4);
EXPECT_EQ(0u, ((uint64_t)invalid_offset) % 4);
memcpy(buffer, reinterpret_cast<void*>(arm_bti_test_function),
function_range);
page_allocator.SetPermissions(buffer, memory_size,
v8::PageAllocator::Permission::kReadExecute);
BTITestFunction const bti_enabled_fn =
reinterpret_cast<BTITestFunction>(buffer);
EXPECT_EQ(bti_enabled_fn(15), 18);
BTITestFunction const bti_invalid_fn =
reinterpret_cast<BTITestFunction>(buffer + invalid_offset);
if (base::CPU::GetInstanceNoAllocation().has_bti()) {
#if BUILDFLAG(IS_POSIX)
EXPECT_EXIT({ bti_invalid_fn(15); }, testing::KilledBySignal(SIGILL),
"");
#endif
} else {
EXPECT_EQ(bti_invalid_fn(15), 17);
}
page_allocator.FreePages(buffer, memory_size);
}
TEST(V8PlatformAllocatorBTITest, VerifyReadWriteExecutePagesAreNotProtected) {
auto page_allocator = gin::PageAllocator();
auto const memory_size =
partition_alloc::internal::PageAllocationGranularity();
auto const memory_alignment =
partition_alloc::internal::PageAllocationGranularity();
char* const buffer = reinterpret_cast<char*>(page_allocator.AllocatePages(
nullptr, memory_size, memory_alignment,
v8::PageAllocator::Permission::kReadWriteExecute));
ptrdiff_t const function_range =
reinterpret_cast<char*>(arm_bti_test_function_end) -
reinterpret_cast<char*>(arm_bti_test_function);
ptrdiff_t const invalid_offset =
reinterpret_cast<char*>(arm_bti_test_function_invalid_offset) -
reinterpret_cast<char*>(arm_bti_test_function);
EXPECT_EQ(0u, ((uint64_t)buffer) % 4);
EXPECT_EQ(0u, ((uint64_t)function_range) % 4);
EXPECT_EQ(0u, ((uint64_t)invalid_offset) % 4);
memcpy(buffer, reinterpret_cast<void*>(arm_bti_test_function),
function_range);
BTITestFunction const bti_enabled_fn =
reinterpret_cast<BTITestFunction>(buffer);
EXPECT_EQ(bti_enabled_fn(15), 18);
BTITestFunction const bti_invalid_fn =
reinterpret_cast<BTITestFunction>(buffer + invalid_offset);
EXPECT_EQ(bti_invalid_fn(15), 17);
page_allocator.FreePages(buffer, memory_size);
}
#endif
}