2 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
11 # include <sys/mman.h>
13 # include <mach/mach.h>
16 #include "gtest/gtest.h"
18 #include "native_client/src/include/portability_io.h"
19 #include "native_client/src/trusted/desc/linux/nacl_desc_sysv_shm.h"
20 #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
21 #include "native_client/src/trusted/desc/nacl_desc_io.h"
22 #include "native_client/src/trusted/desc/nrd_all_modules.h"
23 #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
24 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
25 #include "native_client/src/trusted/service_runtime/mmap_test_check.h"
26 #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
27 #include "native_client/src/trusted/service_runtime/sel_addrspace.h"
28 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
29 #include "native_client/src/trusted/service_runtime/sys_memory.h"
31 class MmapTest : public testing::Test {
34 virtual void TearDown();
37 void MmapTest::SetUp() {
38 NaClNrdAllModulesInit();
41 void MmapTest::TearDown() {
42 NaClNrdAllModulesFini();
45 // These tests are disabled for ARM because the ARM sandbox is
46 // zero-based, and sel_addrspace_arm.c does not work when allocating a
47 // non-zero-based region.
48 // TODO(mseaborn): Change sel_addrspace_arm.c to work with this test.
49 // However, for now, testing the Linux memory mapping code under
50 // x86-32 and x86-64 gives us good coverage of the Linux code in
52 #if NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm
54 // Check that the untrusted address space really gets freed by trying
55 // to allocate and free a sandbox multiple times. On a 32-bit system,
56 // this would fail if NaClAddrSpaceFree() were a no-op because it
57 // would run out of host address space.
58 TEST_F(MmapTest, TestFreeingAddressSpace) {
59 // We use a small number of iterations because address space
60 // allocation can be very slow on Windows (on the order of 1 second
61 // on the 32-bit Windows bots).
62 for (int count = 0; count < 4; count++) {
64 ASSERT_EQ(NaClAppCtor(&app), 1);
65 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
66 NaClAddrSpaceFree(&app);
70 void MapShmFd(struct NaClApp *nap, uintptr_t addr, size_t shm_size) {
71 struct NaClDescImcShm *shm_desc =
72 (struct NaClDescImcShm *) malloc(sizeof(*shm_desc));
73 ASSERT_TRUE(shm_desc);
74 ASSERT_EQ(NaClDescImcShmAllocCtor(shm_desc, shm_size,
75 /* executable= */ 0), 1);
76 struct NaClDesc *desc = &shm_desc->base;
77 int fd = NaClSetAvail(nap, desc);
79 uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
80 nap, (void *) addr, shm_size,
81 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
82 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED, fd, 0);
83 ASSERT_EQ(mapping_addr, addr);
86 void MapFileFd(struct NaClApp *nap, uintptr_t addr, size_t file_size) {
89 // Open temporary file that is deleted automatically.
90 const char *temp_prefix = "nacl_mmap_test_temp_";
91 char *temp_filename = _tempnam("C:\\Windows\\Temp", temp_prefix);
92 ASSERT_EQ(_sopen_s(&host_fd, temp_filename,
93 _O_RDWR | _O_CREAT | _O_TEMPORARY,
94 _SH_DENYNO, _S_IREAD | _S_IWRITE), 0);
96 char temp_filename[] = "/tmp/nacl_mmap_test_temp_XXXXXX";
97 host_fd = mkstemp(temp_filename);
98 ASSERT_GE(host_fd, 0);
100 ASSERT_EQ(remove(temp_filename), 0);
102 struct NaClHostDesc *host_desc =
103 (struct NaClHostDesc *) malloc(sizeof(*host_desc));
104 ASSERT_TRUE(host_desc);
105 ASSERT_EQ(NaClHostDescPosixTake(host_desc, host_fd, NACL_ABI_O_RDWR), 0);
106 struct NaClDesc *desc = (struct NaClDesc *) NaClDescIoDescMake(host_desc);
108 // Fill the file. On Windows, this is necessary to ensure that NaCl
109 // gives us mappings from the file instead of PROT_NONE-fill.
110 char *buf = new char[file_size];
111 memset(buf, 0, file_size);
112 ssize_t written = NACL_VTBL(NaClDesc, desc)->Write(desc, buf, file_size);
113 ASSERT_EQ(written, (ssize_t) file_size);
116 int fd = NaClSetAvail(nap, desc);
118 uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
119 nap, (void *) addr, file_size,
120 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
121 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED, fd, 0);
122 ASSERT_EQ(mapping_addr, addr);
125 void UnmapMemory(struct NaClApp *nap, uintptr_t addr, size_t size) {
126 // Create dummy NaClAppThread.
127 // TODO(mseaborn): Clean up so that this is not necessary.
128 struct NaClAppThread thread;
129 memset(&thread, 0xff, sizeof(thread));
132 ASSERT_EQ(NaClSysMunmap(&thread, (void *) addr, size), 0);
134 uintptr_t sysaddr = NaClUserToSys(nap, addr);
136 CheckMapping(sysaddr, size, MEM_RESERVE, 0, MEM_PRIVATE);
138 CheckMapping(sysaddr, size, PROT_NONE, MAP_PRIVATE);
140 CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
142 # error Unsupported platform
146 // Test that shared memory mappings can be unmapped by
147 // NaClAddrSpaceFree(). These are different from private memory
148 // mappings because, on Windows, they must be freed with
149 // UnmapViewOfFile() rather than VirtualFree().
150 TEST_F(MmapTest, TestFreeingShmMappings) {
152 ASSERT_EQ(NaClAppCtor(&app), 1);
153 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
155 // Create a shared memory descriptor of arbitrary size and map it at
156 // an arbitrary address.
157 MapShmFd(&app, 0x200000, 0x100000);
159 // Check that we can deallocate the address space.
160 NaClAddrSpaceFree(&app);
163 // Test that unmapping a shared memory mapping does not leave behind
164 // an address space hole.
165 TEST_F(MmapTest, TestUnmapShmMapping) {
167 ASSERT_EQ(NaClAppCtor(&app), 1);
168 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
170 // sel_mem.c does not like having an empty memory map, so create a
171 // dummy entry that we do not later remove.
172 // TODO(mseaborn): Clean up so that this is not necessary.
173 MapShmFd(&app, 0x400000, 0x10000);
175 uintptr_t addr = 0x200000;
176 size_t size = 0x100000;
177 MapShmFd(&app, addr, size);
179 uintptr_t sysaddr = NaClUserToSys(&app, addr);
181 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
183 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
185 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
187 # error Unsupported platform
190 UnmapMemory(&app, addr, size);
192 NaClAddrSpaceFree(&app);
195 // Test that unmapping a file mapping does not leave behind an address
197 TEST_F(MmapTest, TestUnmapFileMapping) {
199 ASSERT_EQ(NaClAppCtor(&app), 1);
200 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
202 // sel_mem.c does not like having an empty memory map, so create a
203 // dummy entry that we do not later remove.
204 // TODO(mseaborn): Clean up so that this is not necessary.
205 MapShmFd(&app, 0x400000, 0x10000);
207 uintptr_t addr = 0x200000;
208 size_t size = 0x100000;
209 MapFileFd(&app, addr, size);
211 uintptr_t sysaddr = NaClUserToSys(&app, addr);
213 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
215 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
217 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
219 # error Unsupported platform
222 UnmapMemory(&app, addr, size);
224 NaClAddrSpaceFree(&app);
227 // Test we can map an anonymous memory mapping and that unmapping it
228 // does not leave behind an address space hole.
229 TEST_F(MmapTest, TestUnmapAnonymousMemoryMapping) {
231 ASSERT_EQ(NaClAppCtor(&app), 1);
232 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
234 // sel_mem.c does not like having an empty memory map, so create a
235 // dummy entry that we do not later remove.
236 // TODO(mseaborn): Clean up so that this is not necessary.
237 MapShmFd(&app, 0x400000, 0x10000);
239 // Create an anonymous memory mapping.
240 uintptr_t addr = 0x200000;
241 size_t size = 0x100000;
242 uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
243 &app, (void *) addr, size,
244 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
245 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
247 ASSERT_EQ(mapping_addr, addr);
249 uintptr_t sysaddr = NaClUserToSys(&app, addr);
251 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_PRIVATE);
253 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE);
255 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
257 # error Unsupported platform
260 UnmapMemory(&app, addr, size);
262 NaClAddrSpaceFree(&app);
265 // Test that changing a protection of a shared memory mapping is reflected
266 // by the underlying OS memory mapping.
267 TEST_F(MmapTest, TestProtectShmMapping) {
269 ASSERT_EQ(NaClAppCtor(&app), 1);
270 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
272 // sel_mem.c does not like having an empty memory map, so create a
273 // dummy entry that we do not later remove.
274 // TODO(mseaborn): Clean up so that this is not necessary.
275 MapShmFd(&app, 0x400000, 0x10000);
277 uintptr_t addr = 0x200000;
278 size_t size = 0x100000;
279 MapShmFd(&app, addr, size);
281 uintptr_t sysaddr = NaClUserToSys(&app, addr);
283 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
285 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
287 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
289 # error Unsupported platform
292 ASSERT_EQ(0, NaClSysMprotectInternal(
293 &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
296 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_MAPPED);
298 CheckMapping(sysaddr, size, PROT_NONE, MAP_SHARED);
300 CheckMapping(sysaddr, size, VM_PROT_NONE, SM_SHARED);
302 # error Unsupported platform
305 NaClAddrSpaceFree(&app);
308 // Test that changing a protection of a file mapping is reflected
309 // by the underlying OS memory mapping.
310 TEST_F(MmapTest, TestProtectFileMapping) {
312 ASSERT_EQ(NaClAppCtor(&app), 1);
313 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
315 // sel_mem.c does not like having an empty memory map, so create a
316 // dummy entry that we do not later remove.
317 // TODO(mseaborn): Clean up so that this is not necessary.
318 MapShmFd(&app, 0x400000, 0x10000);
320 uintptr_t addr = 0x200000;
321 size_t size = 0x100000;
322 MapFileFd(&app, addr, size);
324 uintptr_t sysaddr = NaClUserToSys(&app, addr);
326 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
328 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
330 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
332 # error Unsupported platform
335 ASSERT_EQ(0, NaClSysMprotectInternal(
336 &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
339 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_MAPPED);
341 CheckMapping(sysaddr, size, PROT_NONE, MAP_SHARED);
343 CheckMapping(sysaddr, size, VM_PROT_NONE, SM_PRIVATE);
345 # error Unsupported platform
348 NaClAddrSpaceFree(&app);
351 // Test that changing a protection of a anonymous memory mapping is
352 // reflected by the underlying OS memory mapping.
353 TEST_F(MmapTest, TestProtectAnonymousMemory) {
355 ASSERT_EQ(NaClAppCtor(&app), 1);
356 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
358 // sel_mem.c does not like having an empty memory map, so create a
359 // dummy entry that we do not later remove.
360 // TODO(mseaborn): Clean up so that this is not necessary.
361 MapShmFd(&app, 0x400000, 0x10000);
363 // Create an anonymous memory mapping.
364 uintptr_t addr = 0x200000;
365 size_t size = 0x100000;
366 uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
367 &app, (void *) addr, size,
368 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
369 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
371 ASSERT_EQ(mapping_addr, addr);
373 uintptr_t sysaddr = NaClUserToSys(&app, addr);
375 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_PRIVATE);
377 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE);
379 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
381 # error Unsupported platform
384 ASSERT_EQ(0, NaClSysMprotectInternal(
385 &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
388 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_PRIVATE);
390 CheckMapping(sysaddr, size, PROT_NONE, MAP_PRIVATE);
392 CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
394 # error Unsupported platform
397 NaClAddrSpaceFree(&app);
400 // NaCl uses SysV shared memory only on Linux, because X Windows
403 TEST_F(MmapTest, TestSysvShmMapping) {
405 ASSERT_EQ(NaClAppCtor(&app), 1);
406 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
408 size_t shm_size = 0x12000;
409 size_t rounded_size = NaClRoundAllocPage(shm_size);
410 ASSERT_EQ(rounded_size, (size_t) 0x20000);
412 struct NaClDescSysvShm *shm_desc =
413 (struct NaClDescSysvShm *) malloc(sizeof(*shm_desc));
414 ASSERT_TRUE(NaClDescSysvShmCtor(shm_desc, shm_size));
415 struct NaClDesc *desc = &shm_desc->base;
416 int fd = NaClSetAvail(&app, desc);
418 uintptr_t mapping_addr = 0x200000;
420 // First, map something with PROT_READ, so that we can later check
421 // that this is correctly overwritten by PROT_READ|PROT_WRITE and
422 // PROT_NONE mappings.
423 uintptr_t result_addr = (uint32_t) NaClSysMmapIntern(
424 &app, (void *) mapping_addr, shm_size,
426 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
428 ASSERT_EQ(result_addr, mapping_addr);
430 result_addr = (uint32_t) NaClSysMmapIntern(
431 &app, (void *) mapping_addr, shm_size,
432 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
433 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED, fd, 0);
434 ASSERT_EQ(result_addr, mapping_addr);
435 uintptr_t sysaddr = NaClUserToSys(&app, mapping_addr);
436 // We should see two mappings. The first is the SysV shared memory
437 // segment. The second is padding upto the 64k page size.
438 CheckMapping(sysaddr, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED);
439 CheckMapping(sysaddr + shm_size, rounded_size - shm_size,
440 PROT_NONE, MAP_PRIVATE);
443 NaClAddrSpaceFree(&app);
447 #endif /* NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm */