qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

noexec.c.inc (3517B)


      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 
     19 static void *arch_mcontext_pc(const mcontext_t *ctx);
     20 static int arch_mcontext_arg(const mcontext_t *ctx);
     21 static void arch_flush(void *p, int len);
     22 
     23 /* Testing infrastructure. */
     24 
     25 struct 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 
     36 static void *page_base;
     37 static int page_size;
     38 static const struct noexec_test *current_noexec_test;
     39 
     40 static void handle_err(const char *syscall)
     41 {
     42     printf("[  FAILED  ] %s: %s\n", syscall, strerror(errno));
     43     exit(EXIT_FAILURE);
     44 }
     45 
     46 static 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 
     87 static 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 
    108 static 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 }