xref: /openbsd-src/regress/sys/kern/xonly/xonly.c (revision ff512342186fed5e9a2c01b55f00458c298093e0)
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