1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
/*
* Common code for arch-specific MMU_INST_FETCH fault testing.
*/
#define _GNU_SOURCE
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
/* 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;
}
|