Line data Source code
1 : // Copyright 2017 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/allocation.h"
6 :
7 : #if V8_OS_POSIX
8 : #include <setjmp.h>
9 : #include <signal.h>
10 : #include <unistd.h> // NOLINT
11 : #endif // V8_OS_POSIX
12 :
13 : #include "testing/gtest/include/gtest/gtest.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 : // TODO(eholk): Add a windows version of permissions tests.
19 : #if V8_OS_POSIX
20 : namespace {
21 :
22 : // These tests make sure the routines to allocate memory do so with the correct
23 : // permissions.
24 : //
25 : // Unfortunately, there is no API to find the protection of a memory address,
26 : // so instead we test permissions by installing a signal handler, probing a
27 : // memory location and recovering from the fault.
28 : //
29 : // We don't test the execution permission because to do so we'd have to
30 : // dynamically generate code and test if we can execute it.
31 :
32 2 : class MemoryAllocationPermissionsTest : public ::testing::Test {
33 4 : static void SignalHandler(int signal, siginfo_t* info, void*) {
34 4 : siglongjmp(continuation_, 1);
35 : }
36 : struct sigaction old_action_;
37 : // On Mac, sometimes we get SIGBUS instead of SIGSEGV.
38 : #if V8_OS_MACOSX
39 : struct sigaction old_bus_action_;
40 : #endif
41 :
42 : protected:
43 1 : void SetUp() override {
44 : struct sigaction action;
45 1 : action.sa_sigaction = SignalHandler;
46 1 : sigemptyset(&action.sa_mask);
47 1 : action.sa_flags = SA_SIGINFO;
48 1 : sigaction(SIGSEGV, &action, &old_action_);
49 : #if V8_OS_MACOSX
50 : sigaction(SIGBUS, &action, &old_bus_action_);
51 : #endif
52 1 : }
53 :
54 1 : void TearDown() override {
55 : // Be a good citizen and restore the old signal handler.
56 1 : sigaction(SIGSEGV, &old_action_, nullptr);
57 : #if V8_OS_MACOSX
58 : sigaction(SIGBUS, &old_bus_action_, nullptr);
59 : #endif
60 1 : }
61 :
62 : public:
63 : static sigjmp_buf continuation_;
64 :
65 : enum class MemoryAction { kRead, kWrite };
66 :
67 10 : void ProbeMemory(volatile int* buffer, MemoryAction action,
68 : bool should_succeed) {
69 : const int save_sigs = 1;
70 10 : if (!sigsetjmp(continuation_, save_sigs)) {
71 6 : switch (action) {
72 : case MemoryAction::kRead: {
73 : // static_cast to remove the reference and force a memory read.
74 5 : USE(static_cast<int>(*buffer));
75 5 : break;
76 : }
77 : case MemoryAction::kWrite: {
78 5 : *buffer = 0;
79 5 : break;
80 : }
81 : }
82 6 : if (should_succeed) {
83 12 : SUCCEED();
84 : } else {
85 0 : FAIL();
86 : }
87 6 : return;
88 : }
89 4 : if (should_succeed) {
90 0 : FAIL();
91 : } else {
92 8 : SUCCEED();
93 : }
94 : }
95 :
96 5 : void TestPermissions(PageAllocator::Permission permission, bool can_read,
97 : bool can_write) {
98 : v8::PageAllocator* page_allocator =
99 5 : v8::internal::GetPlatformPageAllocator();
100 5 : const size_t page_size = page_allocator->AllocatePageSize();
101 : int* buffer = static_cast<int*>(AllocatePages(
102 5 : page_allocator, nullptr, page_size, page_size, permission));
103 5 : ProbeMemory(buffer, MemoryAction::kRead, can_read);
104 5 : ProbeMemory(buffer, MemoryAction::kWrite, can_write);
105 5 : CHECK(FreePages(page_allocator, buffer, page_size));
106 5 : }
107 : };
108 :
109 : sigjmp_buf MemoryAllocationPermissionsTest::continuation_;
110 :
111 : } // namespace
112 :
113 15444 : TEST_F(MemoryAllocationPermissionsTest, DoTest) {
114 1 : TestPermissions(PageAllocator::Permission::kNoAccess, false, false);
115 1 : TestPermissions(PageAllocator::Permission::kRead, true, false);
116 1 : TestPermissions(PageAllocator::Permission::kReadWrite, true, true);
117 1 : TestPermissions(PageAllocator::Permission::kReadWriteExecute, true, true);
118 1 : TestPermissions(PageAllocator::Permission::kReadExecute, true, false);
119 1 : }
120 : #endif // V8_OS_POSIX
121 :
122 : // Basic tests of allocation.
123 :
124 : class AllocationTest : public ::testing::Test {};
125 :
126 15443 : TEST(AllocationTest, AllocateAndFree) {
127 1 : size_t page_size = v8::internal::AllocatePageSize();
128 1 : CHECK_NE(0, page_size);
129 :
130 1 : v8::PageAllocator* page_allocator = v8::internal::GetPlatformPageAllocator();
131 :
132 : // A large allocation, aligned at native allocation granularity.
133 : const size_t kAllocationSize = 1 * v8::internal::MB;
134 1 : void* mem_addr = v8::internal::AllocatePages(
135 1 : page_allocator, page_allocator->GetRandomMmapAddr(), kAllocationSize,
136 1 : page_size, PageAllocator::Permission::kReadWrite);
137 1 : CHECK_NOT_NULL(mem_addr);
138 1 : CHECK(v8::internal::FreePages(page_allocator, mem_addr, kAllocationSize));
139 :
140 : // A large allocation, aligned significantly beyond native granularity.
141 : const size_t kBigAlignment = 64 * v8::internal::MB;
142 1 : void* aligned_mem_addr = v8::internal::AllocatePages(
143 : page_allocator,
144 1 : AlignedAddress(page_allocator->GetRandomMmapAddr(), kBigAlignment),
145 1 : kAllocationSize, kBigAlignment, PageAllocator::Permission::kReadWrite);
146 1 : CHECK_NOT_NULL(aligned_mem_addr);
147 1 : CHECK_EQ(aligned_mem_addr, AlignedAddress(aligned_mem_addr, kBigAlignment));
148 1 : CHECK(v8::internal::FreePages(page_allocator, aligned_mem_addr,
149 : kAllocationSize));
150 1 : }
151 :
152 15443 : TEST(AllocationTest, ReserveMemory) {
153 1 : v8::PageAllocator* page_allocator = v8::internal::GetPlatformPageAllocator();
154 1 : size_t page_size = v8::internal::AllocatePageSize();
155 : const size_t kAllocationSize = 1 * v8::internal::MB;
156 1 : void* mem_addr = v8::internal::AllocatePages(
157 1 : page_allocator, page_allocator->GetRandomMmapAddr(), kAllocationSize,
158 1 : page_size, PageAllocator::Permission::kReadWrite);
159 1 : CHECK_NE(0, page_size);
160 1 : CHECK_NOT_NULL(mem_addr);
161 1 : size_t commit_size = page_allocator->CommitPageSize();
162 1 : CHECK(v8::internal::SetPermissions(page_allocator, mem_addr, commit_size,
163 : PageAllocator::Permission::kReadWrite));
164 : // Check whether we can write to memory.
165 : int* addr = static_cast<int*>(mem_addr);
166 1 : addr[v8::internal::KB - 1] = 2;
167 1 : CHECK(v8::internal::SetPermissions(page_allocator, mem_addr, commit_size,
168 : PageAllocator::Permission::kNoAccess));
169 1 : CHECK(v8::internal::FreePages(page_allocator, mem_addr, kAllocationSize));
170 1 : }
171 :
172 : } // namespace internal
173 9264 : } // namespace v8
|