xref: /openbsd-src/regress/sys/kern/noexec/noexec.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: noexec.c,v 1.20 2019/05/10 15:57:39 visa Exp $	*/
2 
3 /*
4  * Copyright (c) 2002,2003 Michael Shalayeff
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/mman.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <signal.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <limits.h>
39 #include <errno.h>
40 #include <err.h>
41 
42 volatile sig_atomic_t fail;
43 int page_size;
44 char label[64] = "non-exec ";
45 
46 #define PAD 64*1024
47 #define	MAXPAGESIZE 16384
48 #define TESTSZ 256	/* assuming the testfly() will fit */
49 u_int64_t data[(PAD + TESTSZ + PAD + MAXPAGESIZE) / 8] = { 0 };
50 u_int64_t bss[(PAD + TESTSZ + PAD + MAXPAGESIZE) / 8];
51 
52 void testfly(void);
53 
54 static void
55 fdcache(void *p, size_t size)
56 {
57 #ifdef __hppa__
58 	__asm volatile(	/* XXX this hardcodes the TESTSZ */
59 	    "fdc,m	%1(%0)\n\t"
60 	    "fdc,m	%1(%0)\n\t"
61 	    "fdc,m	%1(%0)\n\t"
62 	    "fdc,m	%1(%0)\n\t"
63 	    "fdc,m	%1(%0)\n\t"
64 	    "fdc,m	%1(%0)\n\t"
65 	    "fdc,m	%1(%0)\n\t"
66 	    "fdc,m	%1(%0)"
67 	    : "+r" (p) : "r" (32));
68 #endif
69 #ifdef __sparc64__
70 	char *s = p;
71 	int i;
72 
73 	for (i = 0; i < TESTSZ; i += 8)
74 	  __asm volatile("flush %0" : : "r" (s + i) : "memory");
75 #endif
76 }
77 
78 static void
79 sigsegv(int sig, siginfo_t *sip, void *scp)
80 {
81 	_exit(fail);
82 }
83 
84 static int
85 noexec(void *p, size_t size)
86 {
87 	fail = 0;
88 	printf("%s: execute\n", label);
89 	fflush(stdout);
90 	((void (*)(void))p)();
91 
92 	return (1);
93 }
94 
95 static int
96 noexec_mprotect(void *p, size_t size)
97 {
98 
99 	/* here we must fail on segv since we said it gets executable */
100 	fail = 1;
101 	if (mprotect(p, size, PROT_READ|PROT_EXEC) < 0)
102 		err(1, "mprotect 1");
103 	printf("%s: execute\n", label);
104 	fflush(stdout);
105 	((void (*)(void))p)();
106 
107 	/* here we are successful on segv and fail if it still executes */
108 	fail = 0;
109 	if (mprotect(p, size, PROT_READ) < 0)
110 		err(1, "mprotect 2");
111 	printf("%s: catch a signal\n", label);
112 	fflush(stdout);
113 	((void (*)(void))p)();
114 
115 	return (1);
116 }
117 
118 static void *
119 getaddr(void *a)
120 {
121 	void *ret;
122 
123 	/*
124 	 * Compile with -fno-inline to get reasonable result when comparing
125 	 * local variable address with caller's stack.
126 	 */
127 	if ((void *)&ret < a)
128 		ret = (void *)((u_long)&ret - 4 * page_size);
129 	else
130 		ret = (void *)((u_long)&ret + 4 * page_size);
131 
132 	return (void *)((u_long)ret & ~(page_size - 1));
133 }
134 
135 static int
136 noexec_mmap(void *p, size_t size)
137 {
138 	memcpy(p + page_size * 1, p, page_size);
139 	memcpy(p + page_size * 2, p, page_size);
140 	fdcache(p + page_size * 1, TESTSZ);
141 	fdcache(p + page_size * 2, TESTSZ);
142 	if (mprotect(p, size + 2 * page_size, PROT_READ|PROT_EXEC) != 0)
143 		err(1, "mprotect");
144 
145 	/* here we must fail on segv since we said it gets executable */
146 	fail = 1;
147 
148 	printf("%s: execute #1\n", label);
149 	fflush(stdout);
150 	((void (*)(void))p)();
151 
152 	/* unmap the first page to see that the higher page is still exec */
153 	if (munmap(p, page_size) < 0)
154 		err(1, "munmap");
155 
156 	p += page_size;
157 	printf("%s: execute #2\n", label);
158 	fflush(stdout);
159 	((void (*)(void))p)();
160 
161 	/* unmap the last page to see that the lower page is still exec */
162 	if (munmap(p + page_size, page_size) < 0)
163 		err(1, "munmap");
164 
165 	printf("%s: execute #3\n", label);
166 	fflush(stdout);
167 	((void (*)(void))p)();
168 
169 	return (0);
170 }
171 
172 static void
173 usage(void)
174 {
175 	extern char *__progname;
176 	fprintf(stderr, "Usage: %s [-s <size>] -[TDBHS] [-p] [-m]\n",
177 	    __progname);
178 	exit(2);
179 }
180 
181 int
182 main(int argc, char *argv[])
183 {
184 	u_int64_t stack[TESTSZ/8];	/* assuming the testfly() will fit */
185 	struct sigaction sa;
186 	int (*func)(void *, size_t);
187 	size_t size;
188 	char *ep;
189 	void *p, *ptr;
190 	int pflags, ch;
191 
192 	if ((page_size = sysconf(_SC_PAGESIZE)) < 0)
193 		err(1, "sysconf");
194 
195 	setvbuf(stdout, NULL, _IONBF, 0);
196 	setvbuf(stderr, NULL, _IONBF, 0);
197 
198 	p = NULL;
199 	pflags = MAP_PRIVATE|MAP_ANON|MAP_FIXED;
200 	func = &noexec;
201 	size = TESTSZ;
202 	while ((ch = getopt(argc, argv, "TDBHSmps:")) != -1) {
203 		if (p == NULL) {
204 			switch (ch) {
205 			case 'T':
206 				p = &testfly;
207 				pflags &=~ MAP_FIXED;
208 				(void) strlcat(label, "text", sizeof(label));
209 				continue;
210 			case 'D':
211 				p = &data[(PAD + page_size) / 8];
212 				p = (void *)((long)p & ~(page_size - 1));
213 				(void) strlcat(label, "data", sizeof(label));
214 				continue;
215 			case 'B':
216 				p = &bss[(PAD + page_size) / 8];
217 				p = (void *)((long)p & ~(page_size - 1));
218 				(void) strlcat(label, "bss", sizeof(label));
219 				continue;
220 			case 'H':
221 				p = malloc(size + 2 * page_size);
222 				if (p == NULL)
223 					err(2, "malloc");
224 				p += page_size;
225 				p = (void *)((long)p & ~(page_size - 1));
226 				(void) strlcat(label, "heap", sizeof(label));
227 				continue;
228 			case 'S':
229 				p = getaddr(&stack);
230 				pflags |= MAP_STACK;
231 				(void) strlcat(label, "stack", sizeof(label));
232 				continue;
233 			case 's':	/* only valid for heap and size */
234 				size = strtoul(optarg, &ep, 0);
235 				if (size > ULONG_MAX)
236 					errno = ERANGE;
237 				if (errno)
238 					err(1, "invalid size: %s", optarg);
239 				if (*ep)
240 					errx(1, "invalid size: %s", optarg);
241 				continue;
242 			}
243 		}
244 		switch (ch) {
245 		case 'm':
246 			if (p) {
247 				(void) strlcat(label, "-mmap", sizeof(label));
248 			} else {
249 				pflags = MAP_ANON;
250 				func = &noexec_mmap;
251 				(void) strlcat(label, "mmap", sizeof(label));
252 			}
253 			ptr = mmap(p, size + 2 * page_size,
254 			    PROT_READ|PROT_WRITE, pflags, -1, 0LL);
255 			if (ptr == MAP_FAILED) {
256 				err(1, "mmap: addr %p, len %zu, prot %d, "
257 				    "flags %d, fd %d, offset %lld",
258 				    p, size + 2 * page_size,
259 				    PROT_READ|PROT_WRITE, pflags, -1, 0LL);
260 			}
261 			p = ptr;
262 			break;
263 		case 'p':
264 			func = &noexec_mprotect;
265 			(void) strlcat(label, "-mprotect", sizeof(label));
266 			break;
267 		default:
268 			usage();
269 		}
270 	}
271 	argc -= optind;
272 	argv += optind;
273 
274 	if (argc > 0)
275 		usage();
276 
277 	if (p == NULL)
278 		exit(2);
279 
280 	sa.sa_sigaction = &sigsegv;
281 	sa.sa_flags = SA_SIGINFO;
282 	sigemptyset(&sa.sa_mask);
283 	sigaction(SIGSEGV, &sa, NULL);
284 
285 	if (p != &testfly) {
286 		memcpy(p, &testfly, TESTSZ);
287 		fdcache(p, size);
288 	}
289 
290 	exit((*func)(p, size));
291 }
292