/* * Common code for arch-specific MMU_INST_FETCH fault testing. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include /* Forward declarations. */ static void *arch_mcontext_pc(const mcontext_t *ctx); static int arch_mcontext_arg(const mcontext_t *ctx); static void arch_flush(void *p, int len); /* Testing infrastructure. */ struct noexec_test { const char *name; const char *test_code; int test_len; int page_ofs; int entry_ofs; int expected_si_ofs; int expected_pc_ofs; int expected_arg; }; static void *page_base; static int page_size; static const struct noexec_test *current_noexec_test; static void handle_err(const char *syscall) { printf("[ FAILED ] %s: %s\n", syscall, strerror(errno)); exit(EXIT_FAILURE); } static void handle_segv(int sig, siginfo_t *info, void *ucontext) { const struct noexec_test *test = current_noexec_test; const mcontext_t *mc = &((ucontext_t *)ucontext)->uc_mcontext; void *expected_si; void *expected_pc; void *pc; int arg; if (test == NULL) { printf("[ FAILED ] unexpected SEGV\n"); exit(EXIT_FAILURE); } current_noexec_test = NULL; expected_si = page_base + test->expected_si_ofs; if (info->si_addr != expected_si) { printf("[ FAILED ] wrong si_addr (%p != %p)\n", info->si_addr, expected_si); exit(EXIT_FAILURE); } pc = arch_mcontext_pc(mc); expected_pc = page_base + test->expected_pc_ofs; if (pc != expected_pc) { printf("[ FAILED ] wrong pc (%p != %p)\n", pc, expected_pc); exit(EXIT_FAILURE); } arg = arch_mcontext_arg(mc); if (arg != test->expected_arg) { printf("[ FAILED ] wrong arg (%d != %d)\n", arg, test->expected_arg); exit(EXIT_FAILURE); } if (mprotect(page_base, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) { handle_err("mprotect"); } } static void test_noexec_1(const struct noexec_test *test) { void *start = page_base + test->page_ofs; void (*fn)(int arg) = page_base + test->entry_ofs; memcpy(start, test->test_code, test->test_len); arch_flush(start, test->test_len); /* Trigger TB creation in order to test invalidation. */ fn(0); if (mprotect(page_base, page_size, PROT_NONE) < 0) { handle_err("mprotect"); } /* Trigger SEGV and check that handle_segv() ran. */ current_noexec_test = test; fn(0); assert(current_noexec_test == NULL); } static int test_noexec(struct noexec_test *tests, size_t n_tests) { struct sigaction act; size_t i; memset(&act, 0, sizeof(act)); act.sa_sigaction = handle_segv; act.sa_flags = SA_SIGINFO; if (sigaction(SIGSEGV, &act, NULL) < 0) { handle_err("sigaction"); } page_size = getpagesize(); page_base = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (page_base == MAP_FAILED) { handle_err("mmap"); } page_base += page_size; for (i = 0; i < n_tests; i++) { struct noexec_test *test = &tests[i]; printf("[ RUN ] %s\n", test->name); test_noexec_1(test); printf("[ OK ]\n"); } printf("[ PASSED ]\n"); return EXIT_SUCCESS; }