- add third_party src.
[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 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
51 // general.
52 #if NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm
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 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++) {
63     struct NaClApp app;
64     ASSERT_EQ(NaClAppCtor(&app), 1);
65     ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
66     NaClAddrSpaceFree(&app);
67   }
68 }
69
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);
78
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);
84 }
85
86 void MapFileFd(struct NaClApp *nap, uintptr_t addr, size_t file_size) {
87   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   char temp_filename[] = "/tmp/nacl_mmap_test_temp_XXXXXX";
97   host_fd = mkstemp(temp_filename);
98   ASSERT_GE(host_fd, 0);
99 #endif
100   ASSERT_EQ(remove(temp_filename), 0);
101
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);
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   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);
114   delete[] buf;
115
116   int fd = NaClSetAvail(nap, desc);
117
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);
123 }
124
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));
130   thread.nap = nap;
131
132   ASSERT_EQ(NaClSysMunmap(&thread, (void *) addr, size), 0);
133
134   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   CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
141 #else
142 # error Unsupported platform
143 #endif
144 }
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 TEST_F(MmapTest, TestFreeingShmMappings) {
151   struct NaClApp app;
152   ASSERT_EQ(NaClAppCtor(&app), 1);
153   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   MapShmFd(&app, 0x200000, 0x100000);
158
159   // Check that we can deallocate the address space.
160   NaClAddrSpaceFree(&app);
161 }
162
163 // Test that unmapping a shared memory mapping does not leave behind
164 // an address space hole.
165 TEST_F(MmapTest, TestUnmapShmMapping) {
166   struct NaClApp app;
167   ASSERT_EQ(NaClAppCtor(&app), 1);
168   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   MapShmFd(&app, 0x400000, 0x10000);
174
175   uintptr_t addr = 0x200000;
176   size_t size = 0x100000;
177   MapShmFd(&app, addr, size);
178
179   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   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
186 #else
187 # error Unsupported platform
188 #endif
189
190   UnmapMemory(&app, addr, size);
191
192   NaClAddrSpaceFree(&app);
193 }
194
195 // Test that unmapping a file mapping does not leave behind an address
196 // space hole.
197 TEST_F(MmapTest, TestUnmapFileMapping) {
198   struct NaClApp app;
199   ASSERT_EQ(NaClAppCtor(&app), 1);
200   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   MapShmFd(&app, 0x400000, 0x10000);
206
207   uintptr_t addr = 0x200000;
208   size_t size = 0x100000;
209   MapFileFd(&app, addr, size);
210
211   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   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
218 #else
219 # error Unsupported platform
220 #endif
221
222   UnmapMemory(&app, addr, size);
223
224   NaClAddrSpaceFree(&app);
225 }
226
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) {
230   struct NaClApp app;
231   ASSERT_EQ(NaClAppCtor(&app), 1);
232   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   MapShmFd(&app, 0x400000, 0x10000);
238
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,
246       -1, 0);
247   ASSERT_EQ(mapping_addr, addr);
248
249   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   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
256 #else
257 # error Unsupported platform
258 #endif
259
260   UnmapMemory(&app, addr, size);
261
262   NaClAddrSpaceFree(&app);
263 }
264
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) {
268   struct NaClApp app;
269   ASSERT_EQ(NaClAppCtor(&app), 1);
270   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   MapShmFd(&app, 0x400000, 0x10000);
276
277   uintptr_t addr = 0x200000;
278   size_t size = 0x100000;
279   MapShmFd(&app, addr, size);
280
281   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   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_SHARED);
288 #else
289 # error Unsupported platform
290 #endif
291
292   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   CheckMapping(sysaddr, size, VM_PROT_NONE, SM_SHARED);
301 #else
302 # error Unsupported platform
303 #endif
304
305   NaClAddrSpaceFree(&app);
306 }
307
308 // Test that changing a protection of a file mapping is reflected
309 // by the underlying OS memory mapping.
310 TEST_F(MmapTest, TestProtectFileMapping) {
311   struct NaClApp app;
312   ASSERT_EQ(NaClAppCtor(&app), 1);
313   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   MapShmFd(&app, 0x400000, 0x10000);
319
320   uintptr_t addr = 0x200000;
321   size_t size = 0x100000;
322   MapFileFd(&app, addr, size);
323
324   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   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_PRIVATE);
331 #else
332 # error Unsupported platform
333 #endif
334
335   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   CheckMapping(sysaddr, size, VM_PROT_NONE, SM_PRIVATE);
344 #else
345 # error Unsupported platform
346 #endif
347
348   NaClAddrSpaceFree(&app);
349 }
350
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) {
354   struct NaClApp app;
355   ASSERT_EQ(NaClAppCtor(&app), 1);
356   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   MapShmFd(&app, 0x400000, 0x10000);
362
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,
370       -1, 0);
371   ASSERT_EQ(mapping_addr, addr);
372
373   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   CheckMapping(sysaddr, size, VM_PROT_READ | VM_PROT_WRITE, SM_EMPTY);
380 #else
381 # error Unsupported platform
382 #endif
383
384   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   CheckMapping(sysaddr, size, VM_PROT_NONE, SM_EMPTY);
393 #else
394 # error Unsupported platform
395 #endif
396
397   NaClAddrSpaceFree(&app);
398 }
399
400 // NaCl uses SysV shared memory only on Linux, because X Windows
401 // depends on it.
402 #if NACL_LINUX
403 TEST_F(MmapTest, TestSysvShmMapping) {
404   struct NaClApp app;
405   ASSERT_EQ(NaClAppCtor(&app), 1);
406   ASSERT_EQ(NaClAllocAddrSpace(&app), LOAD_OK);
407
408   size_t shm_size = 0x12000;
409   size_t rounded_size = NaClRoundAllocPage(shm_size);
410   ASSERT_EQ(rounded_size, (size_t) 0x20000);
411
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);
417
418   uintptr_t mapping_addr = 0x200000;
419
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,
425       NACL_ABI_PROT_READ,
426       NACL_ABI_MAP_FIXED | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
427       -1, 0);
428   ASSERT_EQ(result_addr, mapping_addr);
429
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);
441
442   NaClDescUnref(desc);
443   NaClAddrSpaceFree(&app);
444 }
445 #endif
446
447 #endif /* NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm */