1/* 2 * Common code for arch-specific MMU_INST_FETCH fault testing. 3 */ 4 5#define _GNU_SOURCE 6 7#include <assert.h> 8#include <signal.h> 9#include <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12#include <errno.h> 13#include <unistd.h> 14#include <sys/mman.h> 15#include <sys/ucontext.h> 16 17/* Forward declarations. */ 18 19static void *arch_mcontext_pc(const mcontext_t *ctx); 20static int arch_mcontext_arg(const mcontext_t *ctx); 21static void arch_flush(void *p, int len); 22 23/* Testing infrastructure. */ 24 25struct noexec_test { 26 const char *name; 27 const char *test_code; 28 int test_len; 29 int page_ofs; 30 int entry_ofs; 31 int expected_si_ofs; 32 int expected_pc_ofs; 33 int expected_arg; 34}; 35 36static void *page_base; 37static int page_size; 38static const struct noexec_test *current_noexec_test; 39 40static void handle_err(const char *syscall) 41{ 42 printf("[ FAILED ] %s: %s\n", syscall, strerror(errno)); 43 exit(EXIT_FAILURE); 44} 45 46static void handle_segv(int sig, siginfo_t *info, void *ucontext) 47{ 48 const struct noexec_test *test = current_noexec_test; 49 const mcontext_t *mc = &((ucontext_t *)ucontext)->uc_mcontext; 50 void *expected_si; 51 void *expected_pc; 52 void *pc; 53 int arg; 54 55 if (test == NULL) { 56 printf("[ FAILED ] unexpected SEGV\n"); 57 exit(EXIT_FAILURE); 58 } 59 current_noexec_test = NULL; 60 61 expected_si = page_base + test->expected_si_ofs; 62 if (info->si_addr != expected_si) { 63 printf("[ FAILED ] wrong si_addr (%p != %p)\n", 64 info->si_addr, expected_si); 65 exit(EXIT_FAILURE); 66 } 67 68 pc = arch_mcontext_pc(mc); 69 expected_pc = page_base + test->expected_pc_ofs; 70 if (pc != expected_pc) { 71 printf("[ FAILED ] wrong pc (%p != %p)\n", pc, expected_pc); 72 exit(EXIT_FAILURE); 73 } 74 75 arg = arch_mcontext_arg(mc); 76 if (arg != test->expected_arg) { 77 printf("[ FAILED ] wrong arg (%d != %d)\n", arg, test->expected_arg); 78 exit(EXIT_FAILURE); 79 } 80 81 if (mprotect(page_base, page_size, 82 PROT_READ | PROT_WRITE | PROT_EXEC) < 0) { 83 handle_err("mprotect"); 84 } 85} 86 87static void test_noexec_1(const struct noexec_test *test) 88{ 89 void *start = page_base + test->page_ofs; 90 void (*fn)(int arg) = page_base + test->entry_ofs; 91 92 memcpy(start, test->test_code, test->test_len); 93 arch_flush(start, test->test_len); 94 95 /* Trigger TB creation in order to test invalidation. */ 96 fn(0); 97 98 if (mprotect(page_base, page_size, PROT_NONE) < 0) { 99 handle_err("mprotect"); 100 } 101 102 /* Trigger SEGV and check that handle_segv() ran. */ 103 current_noexec_test = test; 104 fn(0); 105 assert(current_noexec_test == NULL); 106} 107 108static int test_noexec(struct noexec_test *tests, size_t n_tests) 109{ 110 struct sigaction act; 111 size_t i; 112 113 memset(&act, 0, sizeof(act)); 114 act.sa_sigaction = handle_segv; 115 act.sa_flags = SA_SIGINFO; 116 if (sigaction(SIGSEGV, &act, NULL) < 0) { 117 handle_err("sigaction"); 118 } 119 120 page_size = getpagesize(); 121 page_base = mmap(NULL, 2 * page_size, 122 PROT_READ | PROT_WRITE | PROT_EXEC, 123 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 124 if (page_base == MAP_FAILED) { 125 handle_err("mmap"); 126 } 127 page_base += page_size; 128 129 for (i = 0; i < n_tests; i++) { 130 struct noexec_test *test = &tests[i]; 131 132 printf("[ RUN ] %s\n", test->name); 133 test_noexec_1(test); 134 printf("[ OK ]\n"); 135 } 136 137 printf("[ PASSED ]\n"); 138 return EXIT_SUCCESS; 139} 140