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/MIPS because the ARM/MIPS sandboxes are
46 // zero-based, and sel_addrspace_(arm/mips).c do 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 && \
53 NACL_ARCH(NACL_BUILD_ARCH) != NACL_mips
55 // Check that the untrusted address space really gets freed by trying
56 // to allocate and free a sandbox multiple times. On a 32-bit system,
57 // this would fail if NaClAddrSpaceFree() were a no-op because it
58 // would run out of host address space.
59 TEST_F(MmapTest, TestFreeingAddressSpace) {
60 // We use a small number of iterations because address space
61 // allocation can be very slow on Windows (on the order of 1 second
62 // on the 32-bit Windows bots).
63 for (int count = 0; count < 4; count++) {
65 ASSERT_EQ(NaClAppCtor(&app), 1);
66 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
67 NaClAddrSpaceFree(&app);
71 void MapShmFd(struct NaClApp *nap, uintptr_t addr, size_t shm_size) {
72 struct NaClDescImcShm *shm_desc =
73 (struct NaClDescImcShm *) malloc(sizeof(*shm_desc));
74 ASSERT_TRUE(shm_desc);
75 ASSERT_EQ(NaClDescImcShmAllocCtor(shm_desc, shm_size,
76 /* executable= */ 0), 1);
77 struct NaClDesc *desc = &shm_desc->base;
78 int fd = NaClAppSetDescAvail(nap, desc);
80 uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
81 nap, (void *) addr, shm_size,
82 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
83 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED, fd, 0);
84 ASSERT_EQ(mapping_addr, addr);
87 void MapFileFd(struct NaClApp *nap, uintptr_t addr, size_t file_size) {
90 // Open temporary file that is deleted automatically.
91 const char *temp_prefix = "nacl_mmap_test_temp_";
92 char *temp_filename = _tempnam("C:\\Windows\\Temp", temp_prefix);
93 ASSERT_EQ(_sopen_s(&host_fd, temp_filename,
94 _O_RDWR | _O_CREAT | _O_TEMPORARY,
95 _SH_DENYNO, _S_IREAD | _S_IWRITE), 0);
97 char temp_filename[] = "/tmp/nacl_mmap_test_temp_XXXXXX";
98 host_fd = mkstemp(temp_filename);
99 ASSERT_GE(host_fd, 0);
101 ASSERT_EQ(remove(temp_filename), 0);
103 struct NaClHostDesc *host_desc =
104 (struct NaClHostDesc *) malloc(sizeof(*host_desc));
105 ASSERT_TRUE(host_desc);
106 ASSERT_EQ(NaClHostDescPosixTake(host_desc, host_fd, NACL_ABI_O_RDWR), 0);
107 struct NaClDesc *desc = (struct NaClDesc *) NaClDescIoDescMake(host_desc);
109 // Fill the file. On Windows, this is necessary to ensure that NaCl
110 // gives us mappings from the file instead of PROT_NONE-fill.
111 char *buf = new char[file_size];
112 memset(buf, 0, file_size);
113 ssize_t written = NACL_VTBL(NaClDesc, desc)->Write(desc, buf, file_size);
114 ASSERT_EQ(written, (ssize_t) file_size);
117 int fd = NaClAppSetDescAvail(nap, desc);
119 uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
120 nap, (void *) addr, file_size,
121 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
122 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED, fd, 0);
123 ASSERT_EQ(mapping_addr, addr);
126 void UnmapMemory(struct NaClApp *nap, uintptr_t addr, size_t size) {
127 // Create dummy NaClAppThread.
128 // TODO(mseaborn): Clean up so that this is not necessary.
129 struct NaClAppThread thread;
130 memset(&thread, 0xff, sizeof(thread));
133 ASSERT_EQ(NaClSysMunmap(&thread, (void *) addr, size), 0);
135 uintptr_t sysaddr = NaClUserToSys(nap, addr);
137 CheckMapping(sysaddr, size, MEM_RESERVE, 0, MEM_PRIVATE);
139 CheckMapping(sysaddr, size, PROT_NONE, MAP_PRIVATE);
141 CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
143 # error Unsupported platform
147 // Test that shared memory mappings can be unmapped by
148 // NaClAddrSpaceFree(). These are different from private memory
149 // mappings because, on Windows, they must be freed with
150 // UnmapViewOfFile() rather than VirtualFree().
151 TEST_F(MmapTest, TestFreeingShmMappings) {
153 ASSERT_EQ(NaClAppCtor(&app), 1);
154 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
156 // Create a shared memory descriptor of arbitrary size and map it at
157 // an arbitrary address.
158 MapShmFd(&app, 0x200000, 0x100000);
160 // Check that we can deallocate the address space.
161 NaClAddrSpaceFree(&app);
164 // Test that unmapping a shared memory mapping does not leave behind
165 // an address space hole.
166 TEST_F(MmapTest, TestUnmapShmMapping) {
168 ASSERT_EQ(NaClAppCtor(&app), 1);
169 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
171 // sel_mem.c does not like having an empty memory map, so create a
172 // dummy entry that we do not later remove.
173 // TODO(mseaborn): Clean up so that this is not necessary.
174 MapShmFd(&app, 0x400000, 0x10000);
176 uintptr_t addr = 0x200000;
177 size_t size = 0x100000;
178 MapShmFd(&app, addr, size);
180 uintptr_t sysaddr = NaClUserToSys(&app, addr);
182 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
184 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
186 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
188 # error Unsupported platform
191 UnmapMemory(&app, addr, size);
193 NaClAddrSpaceFree(&app);
196 // Test that unmapping a file mapping does not leave behind an address
198 TEST_F(MmapTest, TestUnmapFileMapping) {
200 ASSERT_EQ(NaClAppCtor(&app), 1);
201 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
203 // sel_mem.c does not like having an empty memory map, so create a
204 // dummy entry that we do not later remove.
205 // TODO(mseaborn): Clean up so that this is not necessary.
206 MapShmFd(&app, 0x400000, 0x10000);
208 uintptr_t addr = 0x200000;
209 size_t size = 0x100000;
210 MapFileFd(&app, addr, size);
212 uintptr_t sysaddr = NaClUserToSys(&app, addr);
214 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
216 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
218 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
220 # error Unsupported platform
223 UnmapMemory(&app, addr, size);
225 NaClAddrSpaceFree(&app);
228 // Test we can map an anonymous memory mapping and that unmapping it
229 // does not leave behind an address space hole.
230 TEST_F(MmapTest, TestUnmapAnonymousMemoryMapping) {
232 ASSERT_EQ(NaClAppCtor(&app), 1);
233 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
235 // sel_mem.c does not like having an empty memory map, so create a
236 // dummy entry that we do not later remove.
237 // TODO(mseaborn): Clean up so that this is not necessary.
238 MapShmFd(&app, 0x400000, 0x10000);
240 // Create an anonymous memory mapping.
241 uintptr_t addr = 0x200000;
242 size_t size = 0x100000;
243 uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
244 &app, (void *) addr, size,
245 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
246 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
248 ASSERT_EQ(mapping_addr, addr);
250 uintptr_t sysaddr = NaClUserToSys(&app, addr);
252 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_PRIVATE);
254 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE);
256 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
258 # error Unsupported platform
261 UnmapMemory(&app, addr, size);
263 NaClAddrSpaceFree(&app);
266 // Test that changing a protection of a shared memory mapping is reflected
267 // by the underlying OS memory mapping.
268 TEST_F(MmapTest, TestProtectShmMapping) {
270 ASSERT_EQ(NaClAppCtor(&app), 1);
271 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
273 // sel_mem.c does not like having an empty memory map, so create a
274 // dummy entry that we do not later remove.
275 // TODO(mseaborn): Clean up so that this is not necessary.
276 MapShmFd(&app, 0x400000, 0x10000);
278 uintptr_t addr = 0x200000;
279 size_t size = 0x100000;
280 MapShmFd(&app, addr, size);
282 uintptr_t sysaddr = NaClUserToSys(&app, addr);
284 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
286 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
288 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
290 # error Unsupported platform
293 ASSERT_EQ(0, NaClSysMprotectInternal(
294 &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
297 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_MAPPED);
299 CheckMapping(sysaddr, size, PROT_NONE, MAP_SHARED);
301 CheckMapping(sysaddr, size, VM_PROT_NONE, SM_SHARED);
303 # error Unsupported platform
306 NaClAddrSpaceFree(&app);
309 // Test that changing a protection of a file mapping is reflected
310 // by the underlying OS memory mapping.
311 TEST_F(MmapTest, TestProtectFileMapping) {
313 ASSERT_EQ(NaClAppCtor(&app), 1);
314 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
316 // sel_mem.c does not like having an empty memory map, so create a
317 // dummy entry that we do not later remove.
318 // TODO(mseaborn): Clean up so that this is not necessary.
319 MapShmFd(&app, 0x400000, 0x10000);
321 uintptr_t addr = 0x200000;
322 size_t size = 0x100000;
323 MapFileFd(&app, addr, size);
325 uintptr_t sysaddr = NaClUserToSys(&app, addr);
327 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
329 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
331 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
333 # error Unsupported platform
336 ASSERT_EQ(0, NaClSysMprotectInternal(
337 &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
340 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_MAPPED);
342 CheckMapping(sysaddr, size, PROT_NONE, MAP_SHARED);
344 CheckMapping(sysaddr, size, VM_PROT_NONE, SM_PRIVATE);
346 # error Unsupported platform
349 NaClAddrSpaceFree(&app);
352 // Test that changing a protection of a anonymous memory mapping is
353 // reflected by the underlying OS memory mapping.
354 TEST_F(MmapTest, TestProtectAnonymousMemory) {
356 ASSERT_EQ(NaClAppCtor(&app), 1);
357 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
359 // sel_mem.c does not like having an empty memory map, so create a
360 // dummy entry that we do not later remove.
361 // TODO(mseaborn): Clean up so that this is not necessary.
362 MapShmFd(&app, 0x400000, 0x10000);
364 // Create an anonymous memory mapping.
365 uintptr_t addr = 0x200000;
366 size_t size = 0x100000;
367 uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
368 &app, (void *) addr, size,
369 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
370 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
372 ASSERT_EQ(mapping_addr, addr);
374 uintptr_t sysaddr = NaClUserToSys(&app, addr);
376 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_PRIVATE);
378 CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE);
380 CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
382 # error Unsupported platform
385 ASSERT_EQ(0, NaClSysMprotectInternal(
386 &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
389 CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_PRIVATE);
391 CheckMapping(sysaddr, size, PROT_NONE, MAP_PRIVATE);
393 CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
395 # error Unsupported platform
398 NaClAddrSpaceFree(&app);
401 // NaCl uses SysV shared memory only on Linux, because X Windows
404 TEST_F(MmapTest, TestSysvShmMapping) {
406 ASSERT_EQ(NaClAppCtor(&app), 1);
407 ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
409 size_t shm_size = 0x12000;
410 size_t rounded_size = NaClRoundAllocPage(shm_size);
411 ASSERT_EQ(rounded_size, (size_t) 0x20000);
413 struct NaClDescSysvShm *shm_desc =
414 (struct NaClDescSysvShm *) malloc(sizeof(*shm_desc));
415 ASSERT_TRUE(NaClDescSysvShmCtor(shm_desc, shm_size));
416 struct NaClDesc *desc = &shm_desc->base;
417 int fd = NaClAppSetDescAvail(&app, desc);
419 uintptr_t mapping_addr = 0x200000;
421 // First, map something with PROT_READ, so that we can later check
422 // that this is correctly overwritten by PROT_READ|PROT_WRITE and
423 // PROT_NONE mappings.
424 uintptr_t result_addr = (uint32_t) NaClSysMmapIntern(
425 &app, (void *) mapping_addr, shm_size,
427 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
429 ASSERT_EQ(result_addr, mapping_addr);
431 result_addr = (uint32_t) NaClSysMmapIntern(
432 &app, (void *) mapping_addr, shm_size,
433 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
434 NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED, fd, 0);
435 ASSERT_EQ(result_addr, mapping_addr);
436 uintptr_t sysaddr = NaClUserToSys(&app, mapping_addr);
437 // We should see two mappings. The first is the SysV shared memory
438 // segment. The second is padding upto the 64k page size.
439 CheckMapping(sysaddr, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED);
440 CheckMapping(sysaddr + shm_size, rounded_size - shm_size,
441 PROT_NONE, MAP_PRIVATE);
444 NaClAddrSpaceFree(&app);
448 #endif /* NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm */