Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / native_client / tests / mmap / mmap_test.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 <assert.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <limits.h>
11 #include <setjmp.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <sys/mman.h>
17 #include <unistd.h>
18
19 #include "native_client/src/include/nacl_assert.h"
20 #include "native_client/src/include/nacl/nacl_exception.h"
21
22
23 #define PRINT_HEADER 0
24 #define TEXT_LINE_SIZE 1024
25
26 const char *example_file;
27 const char *tmp_dir;
28
29 /*
30  * function failed(testname, msg)
31  *   print failure message and exit with a return code of -1
32  */
33
34 bool failed(const char *testname, const char *msg) {
35   printf("TEST FAILED: %s: %s\n", testname, msg);
36   return false;
37 }
38
39 /*
40  * function passed(testname, msg)
41  *   print success message
42  */
43
44 bool passed(const char *testname, const char *msg) {
45   printf("TEST PASSED: %s: %s\n", testname, msg);
46   return true;
47 }
48
49
50 static jmp_buf g_jmp_buf;
51
52 static void exception_handler(struct NaClExceptionContext *context) {
53   /* We got an exception as expected.  Return from the handler. */
54   int rc = nacl_exception_clear_flag();
55   assert(rc == 0);
56   longjmp(g_jmp_buf, 1);
57 }
58
59 static void assert_addr_is_unreadable(volatile char *addr) {
60   /*
61    * TODO(mseaborn): It would be better to use Valgrind annotations to
62    * turn off the memory access checks temporarily.
63    */
64   if (getenv("RUNNING_ON_VALGRIND") != NULL) {
65     fprintf(stderr, "Skipping assert_addr_is_unreadable() under Valgrind\n");
66     return;
67   }
68   /*
69    * Non-SFI Mode nonsfi_loader with host libc does not have signal
70    * handler implementation.
71    */
72   if (!USE_NEWLIB_NONSFI_LOADER) {
73     fprintf(stderr, "Skipping assert_addr_is_unreadable() under "
74             "nonsfi_loader with host libc\n");
75     return;
76   }
77
78   int rc = nacl_exception_set_handler(exception_handler);
79   assert(rc == 0);
80   if (!setjmp(g_jmp_buf)) {
81     char value = *addr;
82     /* If we reach here, the assertion failed. */
83     fprintf(stderr, "Address %p was readable, and contained %i\n",
84             addr, value);
85     exit(1);
86   }
87   /*
88    * Clean up: Unregister the exception handler so that we do not
89    * accidentally return through g_jmp_buf if an exception occurs.
90    */
91   rc = nacl_exception_set_handler(NULL);
92   assert(rc == 0);
93 }
94
95 static void assert_addr_is_unwritable(volatile char *addr, char value) {
96   /*
97    * TODO(mseaborn): It would be better to use Valgrind annotations to
98    * turn off the memory access checks temporarily.
99    */
100   if (getenv("RUNNING_ON_VALGRIND") != NULL) {
101     fprintf(stderr, "Skipping assert_addr_is_unwritable() under Valgrind\n");
102     return;
103   }
104   if (getenv("RUNNING_ON_ASAN") != NULL) {
105     fprintf(stderr, "Skipping assert_addr_is_unwritable() under ASan\n");
106     return;
107   }
108   /*
109    * Non-SFI Mode nonsfi_loader with host libc does not have signal
110    * handler implementation.
111    */
112   if (!USE_NEWLIB_NONSFI_LOADER) {
113     fprintf(stderr, "Skipping assert_addr_is_unwritable() under "
114             "nonsfi_loader with host libc\n");
115     return;
116   }
117
118   int rc = nacl_exception_set_handler(exception_handler);
119   assert(rc == 0);
120   if (!setjmp(g_jmp_buf)) {
121     *addr = value;
122     /* If we reach here, the assertion failed. */
123     fprintf(stderr, "Address %p was writable, %i was written\n",
124             addr, value);
125     exit(1);
126   }
127   /*
128    * Clean up: Unregister the exception handler so that we do not
129    * accidentally return through g_jmp_buf if an exception occurs.
130    */
131   rc = nacl_exception_set_handler(NULL);
132   assert(rc == 0);
133 }
134
135 static void assert_page_is_allocated(void *addr) {
136   const int kPageSize = getpagesize();
137   assert(((uintptr_t) addr & (kPageSize - 1)) == 0);
138   /*
139    * Try mapping at addr without MAP_FIXED.  If something is already
140    * mapped there, the system will pick another address.  Otherwise,
141    * we will get the address we asked for.
142    */
143   void *result = mmap(addr, kPageSize, PROT_READ | PROT_WRITE,
144                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
145   assert(result != MAP_FAILED);
146   assert(result != addr);
147   int rc = munmap(result, kPageSize);
148   assert(rc == 0);
149 }
150
151 /*
152  * function test*()
153  *
154  *   Simple tests follow below.  Each test may call one or more
155  *   of the functions above.  They all have a boolean return value
156  *   to indicate success (all tests passed) or failure (one or more
157  *   tests failed)  Order matters - the parent should call
158  *   test1() before test2(), and so on.
159  */
160
161 bool test1() {
162   int size = 64 * 1024;  /* we need 64K */
163   void *zeroes;
164   // test simple mmap
165   void *res = NULL;
166   int rv;
167
168   printf("test1\n");
169
170   res = mmap(res, size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
171   if (0 >= res) /* define MAP_FAILED */
172     return false;
173   printf("mmap done\n");
174   zeroes = malloc(size);
175   memset(zeroes, 0, size);
176   if (memcmp(res, zeroes, size)) {
177     printf("memcmp failed\n");
178     return false;
179   }
180
181   rv = munmap(res, 1024);
182   if (rv != 0) {
183     printf("munmap failed\n");
184     return false;
185   }
186   printf("munmap good\n");
187   return true;
188 }
189
190
191
192 /*
193  *   Verify that munmap of executable text pages will fail.
194  */
195
196 bool test2() {
197   int rv;
198
199   printf("test2\n");
200
201   if (NONSFI_MODE) {
202     /*
203      * Unmapping SFI-NaCl's text page would succeed in non-SFI
204      * mode. We skip this test case.
205      */
206     printf("test2 skipped\n");
207     return true;
208   }
209
210   /* text starts at 64K */
211   rv = munmap(reinterpret_cast<void*>(1<<16), (size_t) (1<<16));
212
213   /*
214    * if the munmap succeeds, we probably won't be able to continue to
215    * run....
216    */
217   printf("munmap returned %d\n", rv);
218
219   if (-1 == rv && EINVAL == errno) {
220     printf("munmap good (failed as expected)\n");
221     return true;
222   }
223   printf("munmap should not have succeeded, or failed with wrong error\n");
224   return false;
225 }
226
227 /*
228  *   Verify that mmap into the NULL pointer guard page will fail.
229  */
230
231 bool test3() {
232   void  *res;
233
234   printf("test3\n");
235   if (NONSFI_MODE) {
236     /*
237      * This test checks a security property of the NaCl TCB.  However, when
238      * calling Linux's mmap() syscall directly, we can't necessarily expect
239      * a specific error value for this case.
240      */
241     printf("test3 skipped\n");
242     return true;
243   }
244
245   res = mmap(static_cast<void*>(0), (size_t) (1 << 16),
246              PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0);
247   printf("res = %p\n", res);
248   if (MAP_FAILED == res) {
249     printf("errno = %d\n", errno);
250   }
251   if (MAP_FAILED == res && EINVAL == errno) {
252     printf("mmap okay\n");
253     return true;
254   }
255   printf("mmap should not have succeeded, or failed with wrong error\n");
256   return false;
257 }
258
259 /*
260  *   Verify that mmap/MAP_FIXED with a non-page-aligned address will fail.
261  */
262
263 bool test4() {
264   printf("test4\n");
265   /* First reserve some address space in which to perform the experiment. */
266   char *alloc = (char *) mmap(NULL, 1 << 16, PROT_NONE,
267                               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
268   if (MAP_FAILED == alloc) {
269     printf("mmap failed\n");
270     return false;
271   }
272
273   void *res = mmap((void *) (alloc + 0x100), 1 << 16, PROT_READ,
274                    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
275   if (MAP_FAILED == res && EINVAL == errno) {
276     printf("mmap gave an error as expected\n");
277     return true;
278   }
279   printf("mmap should not have succeeded, or failed with wrong error\n");
280   return false;
281 }
282
283 /*
284  *   Verify that munmap() leaves virtual addresses inaccessible.
285  */
286
287 bool test_munmap() {
288   printf("test_munmap\n");
289   /*
290    * Note that this test could fail if it were run concurrently with
291    * other tests in the same process, because other threads might
292    * mmap() pages at the address we munmap().
293    *
294    * Note that, on Windows, NaCl's munmap() has different code paths
295    * for anonymous and file-backed mappings.  This test case only
296    * covers the anonymous case.  The file-backed case is covered by
297    * test_mmap_end_of_file().
298    */
299   size_t map_size = 0x20000;
300   char *addr = (char *) mmap(NULL, map_size, PROT_READ | PROT_WRITE,
301                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
302   assert(addr != MAP_FAILED);
303   int rc = munmap(addr, map_size);
304   assert(rc == 0);
305   assert_addr_is_unreadable(addr);
306   assert_addr_is_unreadable(addr + 0x1000);
307   assert_addr_is_unreadable(addr + 0x10000);
308   /* Test that munmap() is idempotent. */
309   rc = munmap(addr, map_size);
310   assert(rc == 0);
311   return true;
312 }
313
314 bool test_mmap_zero_size() {
315   /*
316    * This test fails under Non-SFI Mode under ARM QEMU, because ARM QEMU
317    * handles this case incorrectly.
318    */
319   if (NONSFI_MODE)
320     return true;
321
322   void *addr = mmap(NULL, 0, PROT_READ | PROT_WRITE,
323                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
324   ASSERT_EQ(addr, MAP_FAILED);
325   ASSERT_EQ(errno, EINVAL);
326
327   /* Test behaviour when rounding up the size overflows. */
328   addr = mmap(NULL, ~(size_t) 0, PROT_READ | PROT_WRITE,
329               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
330   ASSERT_EQ(addr, MAP_FAILED);
331   ASSERT_EQ(errno, EINVAL);
332
333   return true;
334 }
335
336 bool test_munmap_zero_size() {
337   /* Allocate a valid address to test munmap() with. */
338   size_t map_size = 0x10000;
339   char *addr = (char *) mmap(NULL, map_size, PROT_READ | PROT_WRITE,
340                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
341   assert(addr != MAP_FAILED);
342
343   int rc = munmap(addr, 0);
344   ASSERT_EQ(rc, -1);
345   ASSERT_EQ(errno, EINVAL);
346
347   /* Test behaviour when rounding up the size overflows. */
348   rc = munmap(addr, ~(size_t) 0);
349   ASSERT_EQ(rc, -1);
350   ASSERT_EQ(errno, EINVAL);
351
352   /* Clean up. */
353   rc = munmap(addr, map_size);
354   ASSERT_EQ(rc, 0);
355   return true;
356 }
357
358 /*
359  *   Verify that mprotect() changes the virtual address protection.
360  */
361
362 bool test_mprotect() {
363   printf("test_mprotect\n");
364   /*
365    * Note that, on Windows, NaCl's mprotect() has different code paths
366    * for anonymous and file-backed mappings.  This test case only
367    * covers the anonymous case.
368    */
369   size_t map_size = 0x20000;
370   char *addr = (char *) mmap(NULL, map_size, PROT_READ | PROT_WRITE,
371                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
372   assert(addr != MAP_FAILED);
373   printf("mmap done\n");
374   /* Change the protection to make the page unreadable. */
375   int rc = mprotect(addr, map_size, PROT_NONE);
376   assert(rc == 0);
377   assert_addr_is_unreadable(addr);
378   assert_addr_is_unreadable(addr + 0x1000);
379   assert_addr_is_unreadable(addr + 0x10000);
380   /* Change the protection to make the page accessible again. */
381   rc = mprotect(addr, map_size, PROT_READ | PROT_WRITE);
382   assert(rc == 0);
383   addr[0] = '5';
384   /* Change the protection to make the page read-only. */
385   rc = mprotect(addr, map_size, PROT_READ);
386   assert(rc == 0);
387   assert_addr_is_unwritable(addr, '9');
388   assert('5' == addr[0]);
389   printf("mprotect good\n");
390   /* We can still munmap() the memory. */
391   rc = munmap(addr, map_size);
392   assert(rc == 0);
393   return true;
394 }
395
396 bool test_mprotect_offset() {
397   printf("test_mprotect_offset\n");
398   /*
399    * Note that, on Windows, NaCl's mprotect() has different code paths
400    * for anonymous and file-backed mappings.  This test case only
401    * covers the anonymous case.
402    */
403   size_t map_size = 0x40000;
404   volatile char *addr = (char *) mmap(NULL, map_size, PROT_READ | PROT_WRITE,
405                                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
406   assert(addr != MAP_FAILED);
407   printf("mmap done\n");
408   /* Change the protection to make the pages unreadable. */
409   int rc = mprotect((char *) addr + 0x20000, 0x20000, PROT_NONE);
410   assert(rc == 0);
411   addr[0] = '5';
412   assert('5' == addr[0]);
413   assert_addr_is_unreadable(addr + 0x20000);
414   assert_addr_is_unreadable(addr + 0x30000);
415   /* Change the protection to make the pages read-only. */
416   rc = mprotect((char *) addr, 0x20000, PROT_READ);
417   assert(rc == 0);
418   assert_addr_is_unwritable(addr, '9');
419   assert('5' == addr[0]);
420   /* Change the protection to make the pages accessible again. */
421   rc = mprotect((char *) addr + 0x10000, 0x20000, PROT_READ | PROT_WRITE);
422   assert(rc == 0);
423   *(addr + 0x20000) = '7';
424   printf("mprotect good\n");
425   /* We can still munmap() the memory. */
426   rc = munmap((char *) addr, map_size);
427   assert(rc == 0);
428   return true;
429 }
430
431 /*
432  *   Verify that mprotect() fails when changing protection of unmapped
433  *   memory region.
434  */
435
436 bool test_mprotect_unmapped_memory() {
437   printf("test_mprotect_unmapped_memory\n");
438   /*
439    * Note that, on Windows, NaCl's mprotect() has different code paths
440    * for anonymous and file-backed mappings.  This test case only
441    * covers the anonymous case.
442    */
443   size_t map_size = 0x20000;
444   char *addr = (char *) mmap(NULL, map_size, PROT_READ | PROT_WRITE,
445                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
446   assert(addr != MAP_FAILED);
447   printf("mmap done\n");
448   /* Unmap the mapped memory region. */
449   int rc = munmap(addr, map_size);
450   assert(rc == 0);
451   printf("munmap done\n");
452   /* Change the protection to make the page unreadable. */
453   rc = mprotect(addr, map_size, PROT_NONE);
454   const int kExpectedErrno = NONSFI_MODE ? ENOMEM : EACCES;
455   if (-1 == rc && kExpectedErrno == errno) {
456     printf("mprotect good (failed as expected)\n");
457     return true;
458   }
459   printf("mprotect should not have succeeded, or failed with wrong error\n");
460   return false;
461 }
462
463
464 /*
465  *   Verify that the last page in a file can be mmapped when the file's
466  *   size is not a multiple of the page size.
467  *   Tests for http://code.google.com/p/nativeclient/issues/detail?id=836
468  */
469
470 bool test_mmap_end_of_file() {
471   printf("test_mmap_end_of_file\n");
472   int fd = open(example_file, O_RDONLY);
473   if (fd < 0) {
474     printf("open() failed\n");
475     return false;
476   }
477   size_t map_size = 0x20000;
478   /*
479    * First, map an address range as readable+writable, in order to
480    * check that these mappings are properly overwritten by the second
481    * mmap() call.
482    */
483   char *alloc = (char *) mmap(NULL, map_size, PROT_READ | PROT_WRITE,
484                               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
485   assert(alloc != MAP_FAILED);
486   char *addr = (char *) mmap((void *) alloc, map_size, PROT_READ,
487                              MAP_PRIVATE | MAP_FIXED, fd, 0);
488   assert(addr == alloc);
489   int rc = close(fd);
490   if (rc != 0) {
491     printf("close() failed\n");
492     return false;
493   }
494   /* To avoid line ending issues, this test file contains no newlines. */
495   const char *expected_data =
496     "Test file for mmapping, less than a page in size.";
497   if (memcmp(alloc, expected_data, strlen(expected_data)) != 0) {
498     printf("Unexpected contents: %s\n", alloc);
499     return false;
500   }
501   /* The first 4k page should be readable. */
502   for (size_t i = strlen(expected_data); i < 0x1000; i++) {
503     if (alloc[i] != 0) {
504       printf("Unexpected padding byte: %i\n", alloc[i]);
505       return false;
506     }
507   }
508   /*
509    * Addresses beyond the first 4k should not be readable.  This is
510    * one case where we expose a 4k page size rather than a 64k page
511    * size.  Windows forces us to expose a mixture of 4k and 64k page
512    * sizes for end-of-file mappings.
513    * See http://code.google.com/p/nativeclient/issues/detail?id=824
514    */
515   assert_addr_is_unreadable(alloc + 0x1000);
516   assert_addr_is_unreadable(alloc + 0x2000);
517   assert_addr_is_unreadable(alloc + 0x10000);
518   assert_addr_is_unreadable(alloc + 0x11000);
519   assert_page_is_allocated(alloc);
520   assert_page_is_allocated(alloc + 0x10000);
521   rc = munmap(alloc, map_size);
522   if (rc != 0) {
523     printf("munmap() failed\n");
524     return false;
525   }
526   /* This is similar to test_munmap(), but it covers the file-backed case. */
527   assert_addr_is_unreadable(alloc);
528   assert_addr_is_unreadable(alloc + 0x1000);
529   assert_addr_is_unreadable(alloc + 0x2000);
530   assert_addr_is_unreadable(alloc + 0x10000);
531   assert_addr_is_unreadable(alloc + 0x11000);
532   return true;
533 }
534
535 /*
536  *   Verify mmap with file offsets works properly.
537  */
538
539 bool test_mmap_offset() {
540   printf("test_mmap_offset\n");
541
542   /*
543    * Prepare a file which is filled with raw integer values. These
544    * integer values are their offsets in the file.
545    */
546   char tmp_filename[PATH_MAX] = {};
547   snprintf(tmp_filename, PATH_MAX - 1, "%s/test.txt", tmp_dir);
548   int fd = open(tmp_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
549   ASSERT_GE(fd, 0);
550   const int map_size = 0x10000;
551   for (int i = 0; i < map_size * 2; i += sizeof(i)) {
552     ssize_t written = write(fd, &i, sizeof(i));
553     ASSERT_EQ(written, sizeof(i));
554   }
555   int rc = close(fd);
556   ASSERT_EQ(rc, 0);
557
558   fd = open(tmp_filename, O_RDONLY);
559   ASSERT_GE(fd, 0);
560
561   /* A valid mmap call with an offset specified. */
562   int file_offset = 0x10000;
563   char *addr = (char *) mmap(NULL, map_size, PROT_READ,
564                              MAP_PRIVATE, fd, file_offset);
565   ASSERT_NE(addr, MAP_FAILED);
566   for (int i = 0; i < map_size; i += sizeof(i)) {
567     int expected = i + file_offset;
568     int actual = *(int *) (addr + i);
569     ASSERT_EQ(expected, actual);
570   }
571   rc = munmap(addr, map_size);
572   ASSERT_EQ(rc, 0);
573
574   /* An invalid offset is specified to mmap. */
575   file_offset = 0x100;
576   addr = (char *) mmap(NULL, map_size, PROT_READ,
577                        MAP_PRIVATE, fd, file_offset);
578   ASSERT_MSG(addr == MAP_FAILED && EINVAL == errno,
579              "mmap should not have succeeded, or failed with wrong error");
580
581   rc = close(fd);
582   ASSERT_EQ(rc, 0);
583
584   printf("mmap with offset good\n");
585   return true;
586 }
587
588 /*
589  * function testSuite()
590  *
591  *   Run through a complete sequence of file tests.
592  *
593  * returns true if all tests succeed.  false if one or more fail.
594  */
595
596 bool testSuite() {
597   bool ret = true;
598   // The order of executing these tests matters!
599   ret &= test1();
600   ret &= test2();
601   ret &= test3();
602   ret &= test4();
603
604   ret &= test_munmap();
605   ret &= test_mmap_zero_size();
606   ret &= test_munmap_zero_size();
607   ret &= test_mprotect();
608   ret &= test_mprotect_offset();
609   ret &= test_mprotect_unmapped_memory();
610   ret &= test_mmap_end_of_file();
611   ret &= test_mmap_offset();
612
613   return ret;
614 }
615
616 /*
617  * main entry point.
618  *
619  * run all tests and call system exit with appropriate value
620  *   0 - success, all tests passed.
621  *  -1 - one or more tests failed.
622  */
623
624 int main(const int argc, const char *argv[]) {
625   bool passed;
626
627   if (argc != 3) {
628     printf("Error: Expected test file and temp dir args\n");
629     return 1;
630   }
631   example_file = argv[1];
632   tmp_dir = argv[2];
633
634   // run the full test suite
635   passed = testSuite();
636
637   if (passed) {
638     printf("All tests PASSED\n");
639     exit(0);
640   }
641   printf("One or more tests FAILED\n");
642   exit(-1);
643 }