1 /* $OpenBSD: xonly.c,v 1.2 2023/06/26 19:03:03 guenther Exp $ */
2
3 #include <sys/types.h>
4 #include <sys/mman.h>
5
6 #include <dlfcn.h>
7 #include <err.h>
8 #include <fcntl.h>
9 #include <setjmp.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #define UNREADABLE 0
16 #define READABLE 1
17
18 int main(void);
19 void sigsegv(int);
20 void setup_table(void);
21
22 void *setup_ldso(void);
23 void *setup_mmap_xz(void);
24 void *setup_mmap_x(void);
25 void *setup_mmap_nrx(void);
26 void *setup_mmap_nwx(void);
27 void *setup_mmap_xnwx(void);
28
29 char ***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void));
30
31 struct outcome {
32 int uu;
33 int ku;
34 };
35
36 struct readable {
37 const char *name;
38 void *(*setup)(void);
39 int isfn;
40 void *addr;
41 int skip;
42 struct outcome got;
43 } readables[] = {
44 { "ld.so", setup_ldso, 1, NULL, 0, {} },
45 { "mmap xz", setup_mmap_xz, 0, NULL, 0, {} },
46 { "mmap x", setup_mmap_x, 0, NULL, 0, {} },
47 { "mmap nrx", setup_mmap_nrx, 0, NULL, 0, {} },
48 { "mmap nwx", setup_mmap_nwx, 0, NULL, 0, {} },
49 { "mmap xnwx", setup_mmap_xnwx, 0, NULL, 0, {} },
50 { "main", NULL, 1, &main, 0, {} },
51 { "libc", NULL, 1, &_csu_finish, 0, {} },
52 };
53
54 static struct outcome expectations[2][8] = {
55 #if defined(__aarch64__) || defined(__amd64__)
56 [0] = {
57 /* ld.so */ { UNREADABLE, UNREADABLE },
58 /* mmap xz */ { UNREADABLE, UNREADABLE },
59 /* mmap x */ { UNREADABLE, UNREADABLE },
60 /* mmap nrx */ { UNREADABLE, UNREADABLE },
61 /* mmap nwx */ { UNREADABLE, UNREADABLE },
62 /* mmap xnwx */ { UNREADABLE, UNREADABLE },
63 /* main */ { UNREADABLE, UNREADABLE },
64 /* libc */ { UNREADABLE, UNREADABLE },
65 },
66 #else
67 #error "unknown architecture"
68 #endif
69 #if defined(__amd64__)
70 /* PKU not available. */
71 [1] = {
72 /* ld.so */ { READABLE, UNREADABLE },
73 /* mmap xz */ { UNREADABLE, UNREADABLE },
74 /* mmap x */ { READABLE, READABLE },
75 /* mmap nrx */ { READABLE, READABLE },
76 /* mmap nwx */ { READABLE, READABLE },
77 /* mmap xnwx */ { READABLE, READABLE },
78 /* main */ { READABLE, UNREADABLE },
79 /* libc */ { READABLE, UNREADABLE },
80 },
81 #endif
82 };
83
84 jmp_buf fail;
85
86 void
sigsegv(__unused int signo)87 sigsegv(__unused int signo)
88 {
89 longjmp(fail, 1);
90 }
91
92 void *
setup_mmap_xz(void)93 setup_mmap_xz(void)
94 {
95 return mmap(NULL, getpagesize(), PROT_EXEC,
96 MAP_ANON | MAP_PRIVATE, -1, 0);
97 /* no data written. tests read-fault of an unbacked exec-only page */
98 }
99
100 void *
setup_mmap_x(void)101 setup_mmap_x(void)
102 {
103 char *addr;
104
105 addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
106 MAP_ANON | MAP_PRIVATE, -1, 0);
107 explicit_bzero(addr, getpagesize());
108 mprotect(addr, getpagesize(), PROT_EXEC);
109 return addr;
110 }
111
112 void *
setup_mmap_nrx(void)113 setup_mmap_nrx(void)
114 {
115 char *addr;
116
117 addr = mmap(NULL, getpagesize(), PROT_NONE,
118 MAP_ANON | MAP_PRIVATE, -1, 0);
119 mprotect(addr, getpagesize(), PROT_READ | PROT_WRITE);
120 explicit_bzero(addr, getpagesize());
121 mprotect(addr, getpagesize(), PROT_EXEC);
122 return addr;
123 }
124
125 void *
setup_mmap_nwx(void)126 setup_mmap_nwx(void)
127 {
128 char *addr;
129
130 addr = mmap(NULL, getpagesize(), PROT_NONE,
131 MAP_ANON | MAP_PRIVATE, -1, 0);
132 mprotect(addr, getpagesize(), PROT_WRITE);
133 explicit_bzero(addr, getpagesize());
134 mprotect(addr, getpagesize(), PROT_EXEC);
135 return addr;
136 }
137
138 void *
setup_mmap_xnwx(void)139 setup_mmap_xnwx(void)
140 {
141 char *addr;
142
143 addr = mmap(NULL, getpagesize(), PROT_EXEC,
144 MAP_ANON | MAP_PRIVATE, -1, 0);
145 mprotect(addr, getpagesize(), PROT_NONE);
146 mprotect(addr, getpagesize(), PROT_WRITE);
147 explicit_bzero(addr, getpagesize());
148 mprotect(addr, getpagesize(), PROT_EXEC);
149 return addr;
150 }
151
152 void *
setup_ldso(void)153 setup_ldso(void)
154 {
155 void *dlopenp, *handle;
156
157 handle = dlopen("ld.so", RTLD_NOW);
158 if (handle == NULL)
159 errx(1, "dlopen");
160 dlopenp = dlsym(handle, "dlopen");
161 return dlopenp;
162 }
163
164 void
setup_table(void)165 setup_table(void)
166 {
167 size_t i;
168
169 for (i = 0; i < sizeof(readables)/sizeof(readables[0]); i++) {
170 if (setjmp(fail) == 0) {
171 if (readables[i].setup)
172 readables[i].addr = readables[i].setup();
173 } else {
174 readables[i].skip = 1;
175 }
176 #ifdef __hppa__
177 /* hppa ptable headers point at the instructions */
178 if (readables[i].isfn) {
179 readables[i].addr = (void *)*(u_int *)
180 ((u_int)readables[i].addr & ~3);
181 }
182 #endif
183 }
184 }
185
186 int
main(void)187 main(void)
188 {
189 size_t i;
190 int p[2];
191 int error = 0;
192 const struct outcome *desires = expectations[0];
193
194 #if defined(__amd64__)
195 {
196 uint32_t ebx, ecx, edx;
197 asm("cpuid" : "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (7), "c" (0));
198 if ((ecx & 8) == 0) /* SEFF0ECX_PKU */
199 desires = expectations[1];
200 }
201 #endif
202
203
204 signal(SIGSEGV, sigsegv);
205 signal(SIGBUS, sigsegv);
206
207 setup_table();
208
209 for (i = 0; i < sizeof(readables)/sizeof(readables[0]); i++) {
210 struct readable *r = &readables[i];
211 char c;
212
213 if (r->skip)
214 continue;
215 pipe(p);
216 fcntl(p[0], F_SETFL, O_NONBLOCK);
217
218 if (write(p[1], r->addr, 1) == 1 && read(p[0], &c, 1) == 1)
219 r->got.ku = 1;
220
221 if (setjmp(fail) == 0) {
222 volatile int x = *(int *)(r->addr);
223 (void)x;
224 r->got.uu = 1;
225 }
226
227 close(p[0]);
228 close(p[1]);
229 }
230
231 printf("%-16s %18s userland kernel\n", "", "");
232 for (i = 0; i < sizeof(readables)/sizeof(readables[0]); i++) {
233 struct readable *r = &readables[i];
234
235 if (r->skip) {
236 printf("%-16s %18p %-12s %-12s\n", r->name, r->addr,
237 "skipped", "skipped");
238 } else {
239 const struct outcome *want = &desires[i];
240
241 if (r->got.uu != want->uu || r->got.ku != want->ku)
242 error++;
243
244 printf("%-16s %18p %s%-10s %s%-10s\n",
245 r->name, r->addr,
246 r->got.uu == want->uu ? "P " : "F ",
247 r->got.uu ? "readable" : "unreadable",
248 r->got.ku == want->ku ? "P " : "F ",
249 r->got.ku ? "readable" : "unreadable");
250 }
251 }
252 return error;
253 }
254