1 : /*
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.
5 : */
6 :
7 : #include <stdio.h>
8 : #include <stdlib.h>
9 :
10 : #if NACL_LINUX
11 : # include <sys/mman.h>
12 : #elif NACL_OSX
13 : # include <mach/mach.h>
14 : #endif
15 :
16 : #include "gtest/gtest.h"
17 :
18 : #include "native_client/src/include/portability_io.h"
19 : #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
20 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
21 : #include "native_client/src/trusted/desc/nrd_all_modules.h"
22 : #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
23 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
24 : #include "native_client/src/trusted/service_runtime/mmap_test_check.h"
25 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
26 : #include "native_client/src/trusted/service_runtime/sel_addrspace.h"
27 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
28 : #include "native_client/src/trusted/service_runtime/sys_memory.h"
29 :
30 16 : class MmapTest : public testing::Test {
31 : protected:
32 : virtual void SetUp();
33 : virtual void TearDown();
34 : };
35 :
36 : void MmapTest::SetUp() {
37 8 : NaClNrdAllModulesInit();
38 8 : }
39 :
40 : void MmapTest::TearDown() {
41 8 : NaClNrdAllModulesFini();
42 8 : }
43 :
44 : // These tests are disabled for ARM/MIPS because the ARM/MIPS sandboxes are
45 : // zero-based, and sel_addrspace_(arm/mips).c do not work when allocating a
46 : // non-zero-based region.
47 : // TODO(mseaborn): Change sel_addrspace_arm.c to work with this test.
48 : // However, for now, testing the Linux memory mapping code under
49 : // x86-32 and x86-64 gives us good coverage of the Linux code in
50 : // general.
51 : #if NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm && \
52 : NACL_ARCH(NACL_BUILD_ARCH) != NACL_mips
53 :
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 8 : 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 10 : for (int count = 0; count < 4; count++) {
63 4 : struct NaClApp app;
64 20 : ASSERT_EQ(NaClAppCtor(&app), 1);
65 21 : ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
66 4 : NaClAddrSpaceFree(&app);
67 4 : }
68 1 : }
69 :
70 9 : void MapShmFd(struct NaClApp *nap, uintptr_t addr, size_t shm_size) {
71 9 : struct NaClDescImcShm *shm_desc =
72 9 : (struct NaClDescImcShm *) malloc(sizeof(*shm_desc));
73 45 : ASSERT_TRUE(shm_desc);
74 45 : ASSERT_EQ(NaClDescImcShmAllocCtor(shm_desc, shm_size,
75 : /* executable= */ 0), 1);
76 9 : struct NaClDesc *desc = &shm_desc->base;
77 9 : int fd = NaClAppSetDescAvail(nap, desc);
78 :
79 9 : 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 45 : ASSERT_EQ(mapping_addr, addr);
84 9 : }
85 :
86 2 : void MapFileFd(struct NaClApp *nap, uintptr_t addr, size_t file_size) {
87 2 : int host_fd;
88 : #if NACL_WINDOWS
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);
95 : #else
96 2 : char temp_filename[] = "/tmp/nacl_mmap_test_temp_XXXXXX";
97 2 : host_fd = mkstemp(temp_filename);
98 10 : ASSERT_GE(host_fd, 0);
99 : #endif
100 10 : ASSERT_EQ(remove(temp_filename), 0);
101 :
102 2 : struct NaClHostDesc *host_desc =
103 2 : (struct NaClHostDesc *) malloc(sizeof(*host_desc));
104 10 : ASSERT_TRUE(host_desc);
105 10 : ASSERT_EQ(NaClHostDescPosixTake(host_desc, host_fd, NACL_ABI_O_RDWR), 0);
106 2 : struct NaClDesc *desc = (struct NaClDesc *) NaClDescIoDescMake(host_desc);
107 :
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 2 : char *buf = new char[file_size];
111 2 : memset(buf, 0, file_size);
112 2 : ssize_t written = NACL_VTBL(NaClDesc, desc)->Write(desc, buf, file_size);
113 10 : ASSERT_EQ(written, (ssize_t) file_size);
114 4 : delete[] buf;
115 :
116 2 : int fd = NaClAppSetDescAvail(nap, desc);
117 :
118 2 : 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 10 : ASSERT_EQ(mapping_addr, addr);
123 2 : }
124 :
125 3 : 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 3 : struct NaClAppThread thread;
129 3 : memset(&thread, 0xff, sizeof(thread));
130 3 : thread.nap = nap;
131 :
132 18 : ASSERT_EQ(NaClSysMunmap(&thread, (void *) addr, size), 0);
133 :
134 3 : uintptr_t sysaddr = NaClUserToSys(nap, addr);
135 : #if NACL_WINDOWS
136 : CheckMapping(sysaddr, size, MEM_RESERVE, 0, MEM_PRIVATE);
137 : #elif NACL_LINUX
138 : CheckMapping(sysaddr, size, PROT_NONE, MAP_PRIVATE);
139 : #elif NACL_OSX
140 3 : CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
141 : #else
142 : # error Unsupported platform
143 : #endif
144 6 : }
145 :
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 8 : TEST_F(MmapTest, TestFreeingShmMappings) {
151 1 : struct NaClApp app;
152 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
153 6 : ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
154 :
155 : // Create a shared memory descriptor of arbitrary size and map it at
156 : // an arbitrary address.
157 1 : MapShmFd(&app, 0x200000, 0x100000);
158 :
159 : // Check that we can deallocate the address space.
160 1 : NaClAddrSpaceFree(&app);
161 2 : }
162 :
163 : // Test that unmapping a shared memory mapping does not leave behind
164 : // an address space hole.
165 8 : TEST_F(MmapTest, TestUnmapShmMapping) {
166 1 : struct NaClApp app;
167 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
168 6 : ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
169 :
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 1 : MapShmFd(&app, 0x400000, 0x10000);
174 :
175 1 : uintptr_t addr = 0x200000;
176 1 : size_t size = 0x100000;
177 1 : MapShmFd(&app, addr, size);
178 :
179 1 : uintptr_t sysaddr = NaClUserToSys(&app, addr);
180 : #if NACL_WINDOWS
181 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
182 : #elif NACL_LINUX
183 : CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
184 : #elif NACL_OSX
185 1 : CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
186 : #else
187 : # error Unsupported platform
188 : #endif
189 :
190 1 : UnmapMemory(&app, addr, size);
191 :
192 1 : NaClAddrSpaceFree(&app);
193 2 : }
194 :
195 : // Test that unmapping a file mapping does not leave behind an address
196 : // space hole.
197 8 : TEST_F(MmapTest, TestUnmapFileMapping) {
198 1 : struct NaClApp app;
199 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
200 6 : ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
201 :
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 1 : MapShmFd(&app, 0x400000, 0x10000);
206 :
207 1 : uintptr_t addr = 0x200000;
208 1 : size_t size = 0x100000;
209 1 : MapFileFd(&app, addr, size);
210 :
211 1 : uintptr_t sysaddr = NaClUserToSys(&app, addr);
212 : #if NACL_WINDOWS
213 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
214 : #elif NACL_LINUX
215 : CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
216 : #elif NACL_OSX
217 1 : CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
218 : #else
219 : # error Unsupported platform
220 : #endif
221 :
222 1 : UnmapMemory(&app, addr, size);
223 :
224 1 : NaClAddrSpaceFree(&app);
225 2 : }
226 :
227 : // Test we can map an anonymous memory mapping and that unmapping it
228 : // does not leave behind an address space hole.
229 8 : TEST_F(MmapTest, TestUnmapAnonymousMemoryMapping) {
230 1 : struct NaClApp app;
231 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
232 5 : ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
233 :
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 1 : MapShmFd(&app, 0x400000, 0x10000);
238 :
239 : // Create an anonymous memory mapping.
240 1 : uintptr_t addr = 0x200000;
241 1 : size_t size = 0x100000;
242 1 : 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,
246 : -1, 0);
247 6 : ASSERT_EQ(mapping_addr, addr);
248 :
249 1 : uintptr_t sysaddr = NaClUserToSys(&app, addr);
250 : #if NACL_WINDOWS
251 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_PRIVATE);
252 : #elif NACL_LINUX
253 : CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE);
254 : #elif NACL_OSX
255 1 : CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
256 : #else
257 : # error Unsupported platform
258 : #endif
259 :
260 1 : UnmapMemory(&app, addr, size);
261 :
262 1 : NaClAddrSpaceFree(&app);
263 2 : }
264 :
265 : // Test that changing a protection of a shared memory mapping is reflected
266 : // by the underlying OS memory mapping.
267 8 : TEST_F(MmapTest, TestProtectShmMapping) {
268 1 : struct NaClApp app;
269 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
270 5 : ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
271 :
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 1 : MapShmFd(&app, 0x400000, 0x10000);
276 :
277 1 : uintptr_t addr = 0x200000;
278 1 : size_t size = 0x100000;
279 1 : MapShmFd(&app, addr, size);
280 :
281 1 : uintptr_t sysaddr = NaClUserToSys(&app, addr);
282 : #if NACL_WINDOWS
283 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
284 : #elif NACL_LINUX
285 : CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
286 : #elif NACL_OSX
287 1 : CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
288 : #else
289 : # error Unsupported platform
290 : #endif
291 :
292 6 : ASSERT_EQ(0, NaClSysMprotectInternal(
293 : &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
294 :
295 : #if NACL_WINDOWS
296 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_MAPPED);
297 : #elif NACL_LINUX
298 : CheckMapping(sysaddr, size, PROT_NONE, MAP_SHARED);
299 : #elif NACL_OSX
300 1 : CheckMapping(sysaddr, size, VM_PROT_NONE, SM_SHARED);
301 : #else
302 : # error Unsupported platform
303 : #endif
304 :
305 1 : NaClAddrSpaceFree(&app);
306 2 : }
307 :
308 : // Test that changing a protection of a file mapping is reflected
309 : // by the underlying OS memory mapping.
310 8 : TEST_F(MmapTest, TestProtectFileMapping) {
311 1 : struct NaClApp app;
312 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
313 5 : ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
314 :
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 1 : MapShmFd(&app, 0x400000, 0x10000);
319 :
320 1 : uintptr_t addr = 0x200000;
321 1 : size_t size = 0x100000;
322 1 : MapFileFd(&app, addr, size);
323 :
324 1 : uintptr_t sysaddr = NaClUserToSys(&app, addr);
325 : #if NACL_WINDOWS
326 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
327 : #elif NACL_LINUX
328 : CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
329 : #elif NACL_OSX
330 1 : CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
331 : #else
332 : # error Unsupported platform
333 : #endif
334 :
335 6 : ASSERT_EQ(0, NaClSysMprotectInternal(
336 : &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
337 :
338 : #if NACL_WINDOWS
339 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_MAPPED);
340 : #elif NACL_LINUX
341 : CheckMapping(sysaddr, size, PROT_NONE, MAP_SHARED);
342 : #elif NACL_OSX
343 1 : CheckMapping(sysaddr, size, VM_PROT_NONE, SM_PRIVATE);
344 : #else
345 : # error Unsupported platform
346 : #endif
347 :
348 1 : NaClAddrSpaceFree(&app);
349 2 : }
350 :
351 : // Test that changing a protection of a anonymous memory mapping is
352 : // reflected by the underlying OS memory mapping.
353 8 : TEST_F(MmapTest, TestProtectAnonymousMemory) {
354 1 : struct NaClApp app;
355 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
356 5 : ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
357 :
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 1 : MapShmFd(&app, 0x400000, 0x10000);
362 :
363 : // Create an anonymous memory mapping.
364 1 : uintptr_t addr = 0x200000;
365 1 : size_t size = 0x100000;
366 1 : 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,
370 : -1, 0);
371 5 : ASSERT_EQ(mapping_addr, addr);
372 :
373 1 : uintptr_t sysaddr = NaClUserToSys(&app, addr);
374 : #if NACL_WINDOWS
375 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_PRIVATE);
376 : #elif NACL_LINUX
377 : CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE);
378 : #elif NACL_OSX
379 1 : CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
380 : #else
381 : # error Unsupported platform
382 : #endif
383 :
384 6 : ASSERT_EQ(0, NaClSysMprotectInternal(
385 : &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
386 :
387 : #if NACL_WINDOWS
388 : CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_PRIVATE);
389 : #elif NACL_LINUX
390 : CheckMapping(sysaddr, size, PROT_NONE, MAP_PRIVATE);
391 : #elif NACL_OSX
392 1 : CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
393 : #else
394 : # error Unsupported platform
395 : #endif
396 :
397 1 : NaClAddrSpaceFree(&app);
398 2 : }
399 :
400 : #endif /* NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm */
|