10f10de878197f782b4278c38b5b02b68aff80bc
[platform/framework/web/crosswalk.git] / src / native_client / src / trusted / service_runtime / mmap_unittest.cc
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/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"
30
31 class MmapTest : public testing::Test {
32  protected:
33   virtual void SetUp();
34   virtual void TearDown();
35 };
36
37 void MmapTest::SetUp() {
38   NaClNrdAllModulesInit();
39 }
40
41 void MmapTest::TearDown() {
42   NaClNrdAllModulesFini();
43 }
44
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
51 // general.
52 #if NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm && \
53     NACL_ARCH(NACL_BUILD_ARCH) != NACL_mips
54
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++) {
64     struct NaClApp app;
65     ASSERT_EQ(NaClAppCtor(&app), 1);
66     ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
67     NaClAddrSpaceFree(&app);
68   }
69 }
70
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);
79
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);
85 }
86
87 void MapFileFd(struct NaClApp *nap, uintptr_t addr, size_t file_size) {
88   int host_fd;
89 #if NACL_WINDOWS
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);
96 #else
97   char temp_filename[] = "/tmp/nacl_mmap_test_temp_XXXXXX";
98   host_fd = mkstemp(temp_filename);
99   ASSERT_GE(host_fd, 0);
100 #endif
101   ASSERT_EQ(remove(temp_filename), 0);
102
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);
108
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);
115   delete[] buf;
116
117   int fd = NaClAppSetDescAvail(nap, desc);
118
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);
124 }
125
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));
131   thread.nap = nap;
132
133   ASSERT_EQ(NaClSysMunmap(&thread, (void *) addr, size), 0);
134
135   uintptr_t sysaddr = NaClUserToSys(nap, addr);
136 #if NACL_WINDOWS
137   CheckMapping(sysaddr, size, MEM_RESERVE, 0, MEM_PRIVATE);
138 #elif NACL_LINUX
139   CheckMapping(sysaddr, size, PROT_NONE, MAP_PRIVATE);
140 #elif NACL_OSX
141   CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
142 #else
143 # error Unsupported platform
144 #endif
145 }
146
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) {
152   struct NaClApp app;
153   ASSERT_EQ(NaClAppCtor(&app), 1);
154   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
155
156   // Create a shared memory descriptor of arbitrary size and map it at
157   // an arbitrary address.
158   MapShmFd(&app, 0x200000, 0x100000);
159
160   // Check that we can deallocate the address space.
161   NaClAddrSpaceFree(&app);
162 }
163
164 // Test that unmapping a shared memory mapping does not leave behind
165 // an address space hole.
166 TEST_F(MmapTest, TestUnmapShmMapping) {
167   struct NaClApp app;
168   ASSERT_EQ(NaClAppCtor(&app), 1);
169   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
170
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);
175
176   uintptr_t addr = 0x200000;
177   size_t size = 0x100000;
178   MapShmFd(&app, addr, size);
179
180   uintptr_t sysaddr = NaClUserToSys(&app, addr);
181 #if NACL_WINDOWS
182   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
183 #elif NACL_LINUX
184   CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
185 #elif NACL_OSX
186   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
187 #else
188 # error Unsupported platform
189 #endif
190
191   UnmapMemory(&app, addr, size);
192
193   NaClAddrSpaceFree(&app);
194 }
195
196 // Test that unmapping a file mapping does not leave behind an address
197 // space hole.
198 TEST_F(MmapTest, TestUnmapFileMapping) {
199   struct NaClApp app;
200   ASSERT_EQ(NaClAppCtor(&app), 1);
201   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
202
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);
207
208   uintptr_t addr = 0x200000;
209   size_t size = 0x100000;
210   MapFileFd(&app, addr, size);
211
212   uintptr_t sysaddr = NaClUserToSys(&app, addr);
213 #if NACL_WINDOWS
214   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
215 #elif NACL_LINUX
216   CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
217 #elif NACL_OSX
218   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
219 #else
220 # error Unsupported platform
221 #endif
222
223   UnmapMemory(&app, addr, size);
224
225   NaClAddrSpaceFree(&app);
226 }
227
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) {
231   struct NaClApp app;
232   ASSERT_EQ(NaClAppCtor(&app), 1);
233   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
234
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);
239
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,
247       -1, 0);
248   ASSERT_EQ(mapping_addr, addr);
249
250   uintptr_t sysaddr = NaClUserToSys(&app, addr);
251 #if NACL_WINDOWS
252   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_PRIVATE);
253 #elif NACL_LINUX
254   CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE);
255 #elif NACL_OSX
256   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
257 #else
258 # error Unsupported platform
259 #endif
260
261   UnmapMemory(&app, addr, size);
262
263   NaClAddrSpaceFree(&app);
264 }
265
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) {
269   struct NaClApp app;
270   ASSERT_EQ(NaClAppCtor(&app), 1);
271   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
272
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);
277
278   uintptr_t addr = 0x200000;
279   size_t size = 0x100000;
280   MapShmFd(&app, addr, size);
281
282   uintptr_t sysaddr = NaClUserToSys(&app, addr);
283 #if NACL_WINDOWS
284   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
285 #elif NACL_LINUX
286   CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
287 #elif NACL_OSX
288   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
289 #else
290 # error Unsupported platform
291 #endif
292
293   ASSERT_EQ(0, NaClSysMprotectInternal(
294                    &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
295
296 #if NACL_WINDOWS
297   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_MAPPED);
298 #elif NACL_LINUX
299   CheckMapping(sysaddr, size, PROT_NONE, MAP_SHARED);
300 #elif NACL_OSX
301   CheckMapping(sysaddr, size, VM_PROT_NONE, SM_SHARED);
302 #else
303 # error Unsupported platform
304 #endif
305
306   NaClAddrSpaceFree(&app);
307 }
308
309 // Test that changing a protection of a file mapping is reflected
310 // by the underlying OS memory mapping.
311 TEST_F(MmapTest, TestProtectFileMapping) {
312   struct NaClApp app;
313   ASSERT_EQ(NaClAppCtor(&app), 1);
314   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
315
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);
320
321   uintptr_t addr = 0x200000;
322   size_t size = 0x100000;
323   MapFileFd(&app, addr, size);
324
325   uintptr_t sysaddr = NaClUserToSys(&app, addr);
326 #if NACL_WINDOWS
327   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_MAPPED);
328 #elif NACL_LINUX
329   CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_SHARED);
330 #elif NACL_OSX
331   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
332 #else
333 # error Unsupported platform
334 #endif
335
336   ASSERT_EQ(0, NaClSysMprotectInternal(
337                    &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
338
339 #if NACL_WINDOWS
340   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_MAPPED);
341 #elif NACL_LINUX
342   CheckMapping(sysaddr, size, PROT_NONE, MAP_SHARED);
343 #elif NACL_OSX
344   CheckMapping(sysaddr, size, VM_PROT_NONE, SM_PRIVATE);
345 #else
346 # error Unsupported platform
347 #endif
348
349   NaClAddrSpaceFree(&app);
350 }
351
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) {
355   struct NaClApp app;
356   ASSERT_EQ(NaClAppCtor(&app), 1);
357   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
358
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);
363
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,
371       -1, 0);
372   ASSERT_EQ(mapping_addr, addr);
373
374   uintptr_t sysaddr = NaClUserToSys(&app, addr);
375 #if NACL_WINDOWS
376   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_READWRITE, MEM_PRIVATE);
377 #elif NACL_LINUX
378   CheckMapping(sysaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE);
379 #elif NACL_OSX
380   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
381 #else
382 # error Unsupported platform
383 #endif
384
385   ASSERT_EQ(0, NaClSysMprotectInternal(
386                    &app, (uint32_t) addr, size, NACL_ABI_PROT_NONE));
387
388 #if NACL_WINDOWS
389   CheckMapping(sysaddr, size, MEM_COMMIT, PAGE_NOACCESS, MEM_PRIVATE);
390 #elif NACL_LINUX
391   CheckMapping(sysaddr, size, PROT_NONE, MAP_PRIVATE);
392 #elif NACL_OSX
393   CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
394 #else
395 # error Unsupported platform
396 #endif
397
398   NaClAddrSpaceFree(&app);
399 }
400
401 // NaCl uses SysV shared memory only on Linux, because X Windows
402 // depends on it.
403 #if NACL_LINUX
404 TEST_F(MmapTest, TestSysvShmMapping) {
405   struct NaClApp app;
406   ASSERT_EQ(NaClAppCtor(&app), 1);
407   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
408
409   size_t shm_size = 0x12000;
410   size_t rounded_size = NaClRoundAllocPage(shm_size);
411   ASSERT_EQ(rounded_size, (size_t) 0x20000);
412
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);
418
419   uintptr_t mapping_addr = 0x200000;
420
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,
426       NACL_ABI_PROT_READ,
427       NACL_ABI_MAP_FIXED | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
428       -1, 0);
429   ASSERT_EQ(result_addr, mapping_addr);
430
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);
442
443   NaClDescUnref(desc);
444   NaClAddrSpaceFree(&app);
445 }
446 #endif
447
448 #endif /* NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm */