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.
19 #include "native_client/src/include/nacl_assert.h"
20 #include "native_client/src/include/nacl/nacl_exception.h"
23 #define PRINT_HEADER 0
24 #define TEXT_LINE_SIZE 1024
26 const char *example_file;
30 * function failed(testname, msg)
31 * print failure message and exit with a return code of -1
34 bool failed(const char *testname, const char *msg) {
35 printf("TEST FAILED: %s: %s\n", testname, msg);
40 * function passed(testname, msg)
41 * print success message
44 bool passed(const char *testname, const char *msg) {
45 printf("TEST PASSED: %s: %s\n", testname, msg);
50 static jmp_buf g_jmp_buf;
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();
56 longjmp(g_jmp_buf, 1);
59 static void assert_addr_is_unreadable(volatile char *addr) {
61 * TODO(mseaborn): It would be better to use Valgrind annotations to
62 * turn off the memory access checks temporarily.
64 if (getenv("RUNNING_ON_VALGRIND") != NULL) {
65 fprintf(stderr, "Skipping assert_addr_is_unreadable() under Valgrind\n");
69 * Non-SFI Mode nonsfi_loader with host libc does not have signal
70 * handler implementation.
72 if (!USE_NEWLIB_NONSFI_LOADER) {
73 fprintf(stderr, "Skipping assert_addr_is_unreadable() under "
74 "nonsfi_loader with host libc\n");
78 int rc = nacl_exception_set_handler(exception_handler);
80 if (!setjmp(g_jmp_buf)) {
82 /* If we reach here, the assertion failed. */
83 fprintf(stderr, "Address %p was readable, and contained %i\n",
88 * Clean up: Unregister the exception handler so that we do not
89 * accidentally return through g_jmp_buf if an exception occurs.
91 rc = nacl_exception_set_handler(NULL);
95 static void assert_addr_is_unwritable(volatile char *addr, char value) {
97 * TODO(mseaborn): It would be better to use Valgrind annotations to
98 * turn off the memory access checks temporarily.
100 if (getenv("RUNNING_ON_VALGRIND") != NULL) {
101 fprintf(stderr, "Skipping assert_addr_is_unwritable() under Valgrind\n");
104 if (getenv("RUNNING_ON_ASAN") != NULL) {
105 fprintf(stderr, "Skipping assert_addr_is_unwritable() under ASan\n");
109 * Non-SFI Mode nonsfi_loader with host libc does not have signal
110 * handler implementation.
112 if (!USE_NEWLIB_NONSFI_LOADER) {
113 fprintf(stderr, "Skipping assert_addr_is_unwritable() under "
114 "nonsfi_loader with host libc\n");
118 int rc = nacl_exception_set_handler(exception_handler);
120 if (!setjmp(g_jmp_buf)) {
122 /* If we reach here, the assertion failed. */
123 fprintf(stderr, "Address %p was writable, %i was written\n",
128 * Clean up: Unregister the exception handler so that we do not
129 * accidentally return through g_jmp_buf if an exception occurs.
131 rc = nacl_exception_set_handler(NULL);
135 static void assert_page_is_allocated(void *addr) {
136 const int kPageSize = getpagesize();
137 assert(((uintptr_t) addr & (kPageSize - 1)) == 0);
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.
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);
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.
162 int size = 64 * 1024; /* we need 64K */
170 res = mmap(res, size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
171 if (0 >= res) /* define MAP_FAILED */
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");
181 rv = munmap(res, 1024);
183 printf("munmap failed\n");
186 printf("munmap good\n");
193 * Verify that munmap of executable text pages will fail.
203 * Unmapping SFI-NaCl's text page would succeed in non-SFI
204 * mode. We skip this test case.
206 printf("test2 skipped\n");
210 /* text starts at 64K */
211 rv = munmap(reinterpret_cast<void*>(1<<16), (size_t) (1<<16));
214 * if the munmap succeeds, we probably won't be able to continue to
217 printf("munmap returned %d\n", rv);
219 if (-1 == rv && EINVAL == errno) {
220 printf("munmap good (failed as expected)\n");
223 printf("munmap should not have succeeded, or failed with wrong error\n");
228 * Verify that mmap into the NULL pointer guard page will fail.
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.
241 printf("test3 skipped\n");
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);
251 if (MAP_FAILED == res && EINVAL == errno) {
252 printf("mmap okay\n");
255 printf("mmap should not have succeeded, or failed with wrong error\n");
260 * Verify that mmap/MAP_FIXED with a non-page-aligned address will fail.
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");
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");
279 printf("mmap should not have succeeded, or failed with wrong error\n");
284 * Verify that munmap() leaves virtual addresses inaccessible.
288 printf("test_munmap\n");
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().
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().
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);
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);
314 bool test_mmap_zero_size() {
316 * This test fails under Non-SFI Mode under ARM QEMU, because ARM QEMU
317 * handles this case incorrectly.
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);
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);
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);
343 int rc = munmap(addr, 0);
345 ASSERT_EQ(errno, EINVAL);
347 /* Test behaviour when rounding up the size overflows. */
348 rc = munmap(addr, ~(size_t) 0);
350 ASSERT_EQ(errno, EINVAL);
353 rc = munmap(addr, map_size);
359 * Verify that mprotect() changes the virtual address protection.
362 bool test_mprotect() {
363 printf("test_mprotect\n");
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.
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);
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);
384 /* Change the protection to make the page read-only. */
385 rc = mprotect(addr, map_size, PROT_READ);
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);
396 bool test_mprotect_offset() {
397 printf("test_mprotect_offset\n");
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.
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);
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);
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);
423 *(addr + 0x20000) = '7';
424 printf("mprotect good\n");
425 /* We can still munmap() the memory. */
426 rc = munmap((char *) addr, map_size);
432 * Verify that mprotect() fails when changing protection of unmapped
436 bool test_mprotect_unmapped_memory() {
437 printf("test_mprotect_unmapped_memory\n");
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.
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);
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");
459 printf("mprotect should not have succeeded, or failed with wrong error\n");
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
470 bool test_mmap_end_of_file() {
471 printf("test_mmap_end_of_file\n");
472 int fd = open(example_file, O_RDONLY);
474 printf("open() failed\n");
477 size_t map_size = 0x20000;
479 * First, map an address range as readable+writable, in order to
480 * check that these mappings are properly overwritten by the second
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);
491 printf("close() failed\n");
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);
501 /* The first 4k page should be readable. */
502 for (size_t i = strlen(expected_data); i < 0x1000; i++) {
504 printf("Unexpected padding byte: %i\n", alloc[i]);
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
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);
523 printf("munmap() failed\n");
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);
536 * Verify mmap with file offsets works properly.
539 bool test_mmap_offset() {
540 printf("test_mmap_offset\n");
543 * Prepare a file which is filled with raw integer values. These
544 * integer values are their offsets in the file.
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);
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));
558 fd = open(tmp_filename, O_RDONLY);
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);
571 rc = munmap(addr, map_size);
574 /* An invalid offset is specified to mmap. */
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");
584 printf("mmap with offset good\n");
589 * function testSuite()
591 * Run through a complete sequence of file tests.
593 * returns true if all tests succeed. false if one or more fail.
598 // The order of executing these tests matters!
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();
619 * run all tests and call system exit with appropriate value
620 * 0 - success, all tests passed.
621 * -1 - one or more tests failed.
624 int main(const int argc, const char *argv[]) {
628 printf("Error: Expected test file and temp dir args\n");
631 example_file = argv[1];
634 // run the full test suite
635 passed = testSuite();
638 printf("All tests PASSED\n");
641 printf("One or more tests FAILED\n");