1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "gin/v8_platform_page_allocator.h"
7 #include "base/check_op.h"
9 #include "build/build_config.h"
10 #include "testing/gtest/include/gtest/gtest.h"
12 // includes for Branch Target Instruction tests
13 #if defined(ARCH_CPU_ARM64) && (OS_LINUX || OS_ANDROID)
14 // BTI is only available for AArch64, relevant platform are Android and Linux
16 #include "base/allocator/partition_allocator/src/partition_alloc/arm_bti_test_functions.h"
17 #include "base/allocator/partition_allocator/src/partition_alloc/page_allocator_constants.h"
18 #if BUILDFLAG(IS_POSIX)
20 #include "testing/gtest/include/gtest/gtest-death-test.h"
22 #endif // defined(ARCH_CPU_ARM64) && (OS_LINUX || OS_ANDROID)
26 TEST(V8PlatformPageAllocatorTest, VerifyGetPageConfig) {
27 auto sut = gin::PageAllocator();
29 CHECK_EQ(sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kNoAccess),
30 partition_alloc::PageAccessibilityConfiguration::kInaccessible);
31 CHECK_EQ(sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kRead),
32 partition_alloc::PageAccessibilityConfiguration::kRead);
34 sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kReadWrite),
35 partition_alloc::PageAccessibilityConfiguration::kReadWrite);
36 CHECK_EQ(sut.GetPageConfigPermissionsForTesting(
37 v8::PageAllocator::kReadWriteExecute),
38 partition_alloc::PageAccessibilityConfiguration::kReadWriteExecute);
40 #if defined(__ARM_FEATURE_BTI_DEFAULT)
42 sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kReadExecute),
43 base::CPU::GetInstanceNoAllocation().has_bti()
44 ? partition_alloc::PageAccessibilityConfiguration::
46 : partition_alloc::PageAccessibilityConfiguration::kReadExecute);
49 sut.GetPageConfigPermissionsForTesting(v8::PageAllocator::kReadExecute),
50 partition_alloc::PageAccessibilityConfiguration::kReadExecute);
53 CHECK_EQ(sut.GetPageConfigPermissionsForTesting(
54 v8::PageAllocator::kNoAccessWillJitLater),
55 partition_alloc::PageAccessibilityConfiguration::
56 kInaccessibleWillJitLater);
59 #if defined(ARCH_CPU_ARM64) && (OS_LINUX || OS_ANDROID)
61 using BTITestFunction = int64_t (*)(int64_t);
63 TEST(V8PlatformPageAllocatorBTITest, VerifyReadExecutePagesAreProtected) {
64 auto page_allocator = gin::PageAllocator();
66 auto const memory_size =
67 partition_alloc::internal::PageAllocationGranularity();
68 auto const memory_alignment =
69 partition_alloc::internal::PageAllocationGranularity();
71 // Next, map some read-write memory and copy some test helper functions there.
72 char* const buffer = reinterpret_cast<char*>(page_allocator.AllocatePages(
73 nullptr, memory_size, memory_alignment,
74 v8::PageAllocator::Permission::kReadWriteExecute));
76 ptrdiff_t const function_range =
77 reinterpret_cast<char*>(arm_bti_test_function_end) -
78 reinterpret_cast<char*>(arm_bti_test_function);
79 ptrdiff_t const invalid_offset =
80 reinterpret_cast<char*>(arm_bti_test_function_invalid_offset) -
81 reinterpret_cast<char*>(arm_bti_test_function);
83 // ensure alignment to 4 bytes required by function call convention
84 EXPECT_EQ(0u, ((uint64_t)buffer) % 4);
85 EXPECT_EQ(0u, ((uint64_t)function_range) % 4);
86 EXPECT_EQ(0u, ((uint64_t)invalid_offset) % 4);
88 memcpy(buffer, reinterpret_cast<void*>(arm_bti_test_function),
91 // Next re-protect the page to the permission level to test
92 page_allocator.SetPermissions(buffer, memory_size,
93 v8::PageAllocator::Permission::kReadExecute);
95 // Attempt to call a function with BTI landing pad.
96 BTITestFunction const bti_enabled_fn =
97 reinterpret_cast<BTITestFunction>(buffer);
99 // bti_enabled_fn must return 18, no matter if BTI is actually enabled or not.
100 EXPECT_EQ(bti_enabled_fn(15), 18);
102 // Next, attempt to call a function without BTI landing pad.
103 BTITestFunction const bti_invalid_fn =
104 reinterpret_cast<BTITestFunction>(buffer + invalid_offset);
106 // Expectation for behaviour of bti_invalid_fn depends on the capabilities of
107 // the actual CPU we are running on. The code that were are trying to execute
108 // is assembly code and always has BTI enabled.
109 if (base::CPU::GetInstanceNoAllocation().has_bti()) {
110 #if BUILDFLAG(IS_POSIX) // signal handling is available on POSIX compliant
112 EXPECT_EXIT({ bti_invalid_fn(15); }, testing::KilledBySignal(SIGILL),
113 ""); // Should crash with SIGILL.
114 #endif // BUILDFLAG(IS_POSIX)
116 EXPECT_EQ(bti_invalid_fn(15), 17);
119 page_allocator.FreePages(buffer, memory_size);
122 TEST(V8PlatformAllocatorBTITest, VerifyReadWriteExecutePagesAreNotProtected) {
123 auto page_allocator = gin::PageAllocator();
125 auto const memory_size =
126 partition_alloc::internal::PageAllocationGranularity();
127 auto const memory_alignment =
128 partition_alloc::internal::PageAllocationGranularity();
130 // Next, map some read-write memory and copy some test helper functions there.
131 char* const buffer = reinterpret_cast<char*>(page_allocator.AllocatePages(
132 nullptr, memory_size, memory_alignment,
133 v8::PageAllocator::Permission::kReadWriteExecute));
135 ptrdiff_t const function_range =
136 reinterpret_cast<char*>(arm_bti_test_function_end) -
137 reinterpret_cast<char*>(arm_bti_test_function);
138 ptrdiff_t const invalid_offset =
139 reinterpret_cast<char*>(arm_bti_test_function_invalid_offset) -
140 reinterpret_cast<char*>(arm_bti_test_function);
142 // ensure alignment to 4 bytes required by function call convention
143 EXPECT_EQ(0u, ((uint64_t)buffer) % 4);
144 EXPECT_EQ(0u, ((uint64_t)function_range) % 4);
145 EXPECT_EQ(0u, ((uint64_t)invalid_offset) % 4);
147 memcpy(buffer, reinterpret_cast<void*>(arm_bti_test_function),
150 // Attempt to call a function with BTI landing pad.
151 BTITestFunction const bti_enabled_fn =
152 reinterpret_cast<BTITestFunction>(buffer);
154 // bti_enabled_fn must return 18, no matter if BTI is actually enabled or not.
155 EXPECT_EQ(bti_enabled_fn(15), 18);
157 // Next, attempt to call a function without BTI landing pad.
158 BTITestFunction const bti_invalid_fn =
159 reinterpret_cast<BTITestFunction>(buffer + invalid_offset);
161 // Since permission kReadWriteExecute wont actually cause BTI to be enabled
162 // for the allocated page, calling this function must return without error.
163 EXPECT_EQ(bti_invalid_fn(15), 17);
165 page_allocator.FreePages(buffer, memory_size);
167 #endif // if defined(ARCH_CPU_ARM64) && (OS_LINUX || OS_ANDROID)