Merge tag 'qemu-macppc-20230206' of https://github.com/mcayland/qemu into staging
[qemu.git] / tests / tcg / multiarch / noexec.c.inc
blob2ef539b7211ab8ad1e5063ce94d4c6eec9c242ae
1 /*
2  * Common code for arch-specific MMU_INST_FETCH fault testing.
3  */
5 #define _GNU_SOURCE
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>
17 /* Forward declarations. */
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);
23 /* Testing infrastructure. */
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;
36 static void *page_base;
37 static int page_size;
38 static const struct noexec_test *current_noexec_test;
40 static void handle_err(const char *syscall)
42     printf("[  FAILED  ] %s: %s\n", syscall, strerror(errno));
43     exit(EXIT_FAILURE);
46 static void handle_segv(int sig, siginfo_t *info, void *ucontext)
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;
55     if (test == NULL) {
56         printf("[  FAILED  ] unexpected SEGV\n");
57         exit(EXIT_FAILURE);
58     }
59     current_noexec_test = NULL;
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     }
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     }
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     }
81     if (mprotect(page_base, page_size,
82                  PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
83         handle_err("mprotect");
84     }
87 static void test_noexec_1(const struct noexec_test *test)
89     void *start = page_base + test->page_ofs;
90     void (*fn)(int arg) = page_base + test->entry_ofs;
92     memcpy(start, test->test_code, test->test_len);
93     arch_flush(start, test->test_len);
95     /* Trigger TB creation in order to test invalidation. */
96     fn(0);
98     if (mprotect(page_base, page_size, PROT_NONE) < 0) {
99         handle_err("mprotect");
100     }
102     /* Trigger SEGV and check that handle_segv() ran. */
103     current_noexec_test = test;
104     fn(0);
105     assert(current_noexec_test == NULL);
108 static int test_noexec(struct noexec_test *tests, size_t n_tests)
110     struct sigaction act;
111     size_t i;
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     }
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;
129     for (i = 0; i < n_tests; i++) {
130         struct noexec_test *test = &tests[i];
132         printf("[ RUN      ] %s\n", test->name);
133         test_noexec_1(test);
134         printf("[       OK ]\n");
135     }
137     printf("[  PASSED  ]\n");
138     return EXIT_SUCCESS;