1 // Copyright 2016 The Fuchsia 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 <errno.h>
6 #include <unistd.h>
7 
8 #include <zircon/process.h>
9 #include <zircon/syscalls.h>
10 #include <zircon/syscalls/object.h>
11 #include <unittest/unittest.h>
12 #include <sys/mman.h>
13 
14 namespace {
15 
16 #if defined(__x86_64__)
17 
18 #include <cpuid.h>
19 
20 // This is based on code from kernel/ which isn't usable by code in system/.
21 enum { X86_CPUID_ADDR_WIDTH = 0x80000008 };
22 
x86_linear_address_width()23 uint32_t x86_linear_address_width() {
24     uint32_t eax, ebx, ecx, edx;
25     __cpuid(X86_CPUID_ADDR_WIDTH, eax, ebx, ecx, edx);
26     return (eax >> 8) & 0xff;
27 }
28 
29 #endif
30 
address_space_limits_test()31 bool address_space_limits_test() {
32     BEGIN_TEST;
33 
34 #if defined(__x86_64__)
35     size_t page_size = getpagesize();
36     zx_handle_t vmo;
37     EXPECT_EQ(zx_vmo_create(page_size, 0, &vmo), ZX_OK);
38     EXPECT_NE(vmo, ZX_HANDLE_INVALID, "vm_object_create");
39 
40     // This is the lowest non-canonical address on x86-64.  We want to
41     // make sure that userland cannot map a page immediately below
42     // this address.  See docs/sysret_problem.md for an explanation of
43     // the reason.
44     uintptr_t noncanon_addr =
45         ((uintptr_t) 1) << (x86_linear_address_width() - 1);
46 
47     zx_info_vmar_t vmar_info;
48     zx_status_t status = zx_object_get_info(zx_vmar_root_self(), ZX_INFO_VMAR,
49                                             &vmar_info, sizeof(vmar_info),
50                                             NULL, NULL);
51     EXPECT_EQ(ZX_OK, status, "get_info");
52 
53     // Check that we cannot map a page ending at |noncanon_addr|.
54     size_t offset = noncanon_addr - page_size - vmar_info.base;
55     uintptr_t addr;
56     status = zx_vmar_map(
57         zx_vmar_root_self(),
58         ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC,
59         offset, vmo, 0, page_size, &addr);
60     EXPECT_EQ(ZX_ERR_INVALID_ARGS, status, "vm_map");
61 
62     // Check that we can map at the next address down.  This helps to
63     // verify that the previous check didn't fail for some unexpected
64     // reason.
65     offset = noncanon_addr - page_size * 2 - vmar_info.base;
66     status = zx_vmar_map(
67         zx_vmar_root_self(),
68         ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC,
69         offset, vmo, 0, page_size, &addr);
70     EXPECT_EQ(ZX_OK, status, "vm_map");
71 
72     // Check that ZX_VM_SPECIFIC fails on already-mapped locations.
73     // Otherwise, the previous mapping could have overwritten
74     // something that was in use, which could cause problems later.
75     status = zx_vmar_map(
76         zx_vmar_root_self(),
77         ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC,
78         offset, vmo, 0, page_size, &addr);
79     EXPECT_EQ(ZX_ERR_NO_MEMORY, status, "vm_map");
80 
81     // Clean up.
82     status = zx_vmar_unmap(zx_vmar_root_self(), addr, page_size);
83     EXPECT_EQ(ZX_OK, status, "vm_unmap");
84     status = zx_handle_close(vmo);
85     EXPECT_EQ(ZX_OK, status, "handle_close");
86 #endif
87 
88     END_TEST;
89 }
90 
mmap_zerofilled_test()91 bool mmap_zerofilled_test() {
92     BEGIN_TEST;
93 
94     char* addr = (char *)mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
95     for (size_t i = 0; i < 16384; i++) {
96         EXPECT_EQ('\0', addr[i], "non-zero memory found");
97     }
98     int unmap_result = munmap(addr, 16384);
99     EXPECT_EQ(0, unmap_result, "munmap should have succeeded");
100 
101     END_TEST;
102 }
103 
mmap_len_test()104 bool mmap_len_test() {
105     BEGIN_TEST;
106 
107     uint32_t* addr = (uint32_t*)mmap(NULL, 0, PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0);
108     auto test_errno = errno;
109     EXPECT_EQ(MAP_FAILED, addr, "mmap should fail when len == 0");
110     EXPECT_EQ(EINVAL, test_errno, "mmap errno should be EINVAL when len == 0");
111 
112     addr = (uint32_t*)mmap(NULL, PTRDIFF_MAX, PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0);
113     test_errno = errno;
114     EXPECT_EQ(MAP_FAILED, addr, "mmap should fail when len >= PTRDIFF_MAX");
115     EXPECT_EQ(ENOMEM, test_errno, "mmap errno should be ENOMEM when len >= PTRDIFF_MAX");
116 
117     END_TEST;
118 }
119 
mmap_offset_test()120 bool mmap_offset_test() {
121     BEGIN_TEST;
122 
123     uint32_t* addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 4);
124     auto test_errno = errno;
125     EXPECT_EQ(MAP_FAILED, addr, "mmap should fail for unaligned offset");
126     EXPECT_EQ(EINVAL, test_errno, "mmap errno should be EINVAL for unaligned offset");
127 
128     END_TEST;
129 }
130 
131 // Define a little fragment of code that we can copy.
132 extern "C" const uint8_t begin_add[], end_add[];
133 __asm__(".pushsection .rodata.add_code\n"
134         ".globl begin_add\n"
135         "begin_add:"
136 #ifdef __x86_64__
137         "mov %rdi, %rax\n"
138         "add %rsi, %rax\n"
139         "ret\n"
140 #elif defined(__aarch64__)
141         "add x0, x0, x1\n"
142         "ret\n"
143 #else
144 # error "what machine?"
145 #endif
146         ".globl end_add\n"
147         "end_add:"
148         ".popsection");
149 
mmap_PROT_EXEC_test()150 bool mmap_PROT_EXEC_test() {
151     BEGIN_TEST;
152 
153     // Allocate a page that will later be made executable.
154     size_t page_size = getpagesize();
155     void* addr = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
156                       MAP_PRIVATE|MAP_ANON, -1, 0);
157     EXPECT_NE(MAP_FAILED, addr,
158               "mmap should have succeeded for PROT_READ|PROT_WRITE");
159 
160     // Copy over code from our address space into the newly allocated memory.
161     ASSERT_LE(static_cast<size_t>(end_add - begin_add), page_size);
162     memcpy(addr, begin_add, end_add - begin_add);
163 
164     // mark the code executable
165     int result = mprotect(addr, page_size, PROT_READ|PROT_EXEC);
166     EXPECT_EQ(0, result, "Unable to mark pages PROT_READ|PROT_EXEC");
167 
168     // Execute the code from our new location.
169     auto add_func = reinterpret_cast<int (*)(int, int)>(
170         reinterpret_cast<uintptr_t>(addr));
171     int add_result = add_func(1, 2);
172 
173     // Check that the result of adding 1+2 is 3.
174     EXPECT_EQ(3, add_result);
175 
176     // Deallocate pages
177     result = munmap(addr, page_size);
178     EXPECT_EQ(0, result, "munmap unexpectedly failed");
179 
180     END_TEST;
181 }
182 
mmap_prot_test()183 bool mmap_prot_test() {
184     BEGIN_TEST;
185 
186     volatile uint32_t* addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
187     EXPECT_NE(MAP_FAILED, addr, "mmap should have succeeded for PROT_NONE");
188 
189     addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0);
190     EXPECT_NE(MAP_FAILED, addr, "mmap failed for read-only alloc");
191 
192     // This is somewhat pointless, to have a private read-only mapping, but we
193     // should be able to read it.
194     EXPECT_EQ(*addr, *addr, "could not read from mmaped address");
195 
196     addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
197     EXPECT_NE(MAP_FAILED, addr, "mmap failed for read-write alloc");
198 
199     // Now we test writing to the mapped memory, and verify that we can read it
200     // back.
201     *addr = 5678u;
202     EXPECT_EQ(5678u, *addr, "writing to address returned by mmap failed");
203 
204     END_TEST;
205 }
206 
mmap_flags_test()207 bool mmap_flags_test() {
208     BEGIN_TEST;
209 
210     uint32_t* addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ, MAP_ANON, -1, 0);
211     auto test_errno = errno;
212     EXPECT_EQ(MAP_FAILED, addr, "mmap should fail without MAP_PRIVATE or MAP_SHARED");
213     EXPECT_EQ(EINVAL, test_errno, "mmap errno should be EINVAL with bad flags");
214 
215     addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ, MAP_PRIVATE|MAP_SHARED|MAP_ANON, -1, 0);
216     test_errno = errno;
217     EXPECT_EQ(MAP_FAILED, addr, "mmap should fail with both MAP_PRIVATE and MAP_SHARED");
218     EXPECT_EQ(EINVAL, test_errno, "mmap errno should be EINVAL with bad flags");
219 
220     addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0);
221     EXPECT_NE(MAP_FAILED, addr, "mmap failed with MAP_PRIVATE flags");
222 
223     addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ, MAP_SHARED|MAP_ANON, -1, 0);
224     EXPECT_NE(MAP_FAILED, addr, "mmap failed with MAP_SHARED flags");
225 
226     END_TEST;
227 }
228 
mprotect_test()229 bool mprotect_test() {
230     BEGIN_TEST;
231 
232     uint32_t* addr = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
233     ASSERT_NE(MAP_FAILED, addr, "mmap failed to map");
234 
235     int page_size = getpagesize();
236     // Should be able to write.
237     *addr = 10;
238     EXPECT_EQ(10u, *addr, "read after write failed");
239 
240     int status = mprotect(addr, page_size, PROT_READ);
241     EXPECT_EQ(0, status, "mprotect failed to downgrade to read-only");
242 
243     ASSERT_DEATH([](void* crashaddr) {
244         uint32_t *intptr = static_cast<uint32_t *>(crashaddr);
245         *intptr = 12;
246     }, addr, "write to addr should have caused a crash");
247 
248     status = mprotect(addr, page_size, PROT_WRITE);
249     auto test_errno = errno;
250     EXPECT_EQ(-1, status, "mprotect should fail for write-only");
251     EXPECT_EQ(ENOTSUP, test_errno, "mprotect should return ENOTSUP for write-only");
252 
253     status = mprotect(addr, page_size, PROT_NONE);
254     test_errno = errno;
255     EXPECT_EQ(0, status, "mprotect should succeed for PROT_NONE");
256 
257     END_TEST;
258 }
259 
260 }
261 
262 BEGIN_TEST_CASE(memory_mapping_tests)
263 RUN_TEST(address_space_limits_test);
264 RUN_TEST(mmap_zerofilled_test);
265 RUN_TEST(mmap_len_test);
266 RUN_TEST(mmap_PROT_EXEC_test);
267 RUN_TEST(mmap_offset_test);
268 RUN_TEST(mmap_prot_test);
269 RUN_TEST(mmap_flags_test);
270 RUN_TEST_ENABLE_CRASH_HANDLER(mprotect_test);
END_TEST_CASE(memory_mapping_tests)271 END_TEST_CASE(memory_mapping_tests)
272 
273 #ifndef BUILD_COMBINED_TESTS
274 int main(int argc, char** argv) {
275     bool success = unittest_run_all_tests(argc, argv);
276     return success ? 0 : -1;
277 }
278 #endif
279