xref: /openbsd-src/regress/sys/kern/noexec/noexec.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: noexec.c,v 1.10 2007/12/27 17:56:44 deraadt 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 8192
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 }
70 
71 static void
72 sigsegv(int sig, siginfo_t *sip, void *scp)
73 {
74 	_exit(fail);
75 }
76 
77 static int
78 noexec(void *p, size_t size)
79 {
80 	fail = 0;
81 	printf("%s: execute\n", label);
82 	fflush(stdout);
83 	((void (*)(void))p)();
84 
85 	return (1);
86 }
87 
88 static int
89 noexec_mprotect(void *p, size_t size)
90 {
91 
92 	/* here we must fail on segv since we said it gets executable */
93 	fail = 1;
94 	if (mprotect(p, size, PROT_READ|PROT_EXEC) < 0)
95 		err(1, "mprotect 1");
96 	printf("%s: execute\n", label);
97 	fflush(stdout);
98 	((void (*)(void))p)();
99 
100 	/* here we are successful on segv and fail if it still executes */
101 	fail = 0;
102 	if (mprotect(p, size, PROT_READ) < 0)
103 		err(1, "mprotect 2");
104 	printf("%s: catch a signal\n", label);
105 	fflush(stdout);
106 	((void (*)(void))p)();
107 
108 	return (1);
109 }
110 
111 static void *
112 getaddr(void *a)
113 {
114 	void *ret;
115 
116 	if ((void *)&ret < a)
117 		ret = (void *)((u_long)&ret - 4 * page_size);
118 	else
119 		ret = (void *)((u_long)&ret + 4 * page_size);
120 
121 	return (void *)((u_long)ret & ~(page_size - 1));
122 }
123 
124 static int
125 noexec_mmap(void *p, size_t size)
126 {
127 	memcpy(p + page_size * 1, p, page_size);
128 	memcpy(p + page_size * 2, p, page_size);
129 	fdcache(p + page_size * 1, TESTSZ);
130 	fdcache(p + page_size * 2, TESTSZ);
131 
132 	/* here we must fail on segv since we said it gets executable */
133 	fail = 1;
134 
135 	printf("%s: execute #1\n", label);
136 	fflush(stdout);
137 	((void (*)(void))p)();
138 
139 	/* unmap the first page to see that the higher page is still exec */
140 	if (munmap(p, page_size) < 0)
141 		err(1, "munmap");
142 
143 	p += page_size;
144 	printf("%s: execute #2\n", label);
145 	fflush(stdout);
146 	((void (*)(void))p)();
147 
148 	/* unmap the last page to see that the lower page is still exec */
149 	if (munmap(p + page_size, page_size) < 0)
150 		err(1, "munmap");
151 
152 	printf("%s: execute #3\n", label);
153 	fflush(stdout);
154 	((void (*)(void))p)();
155 
156 	return (0);
157 }
158 
159 static void
160 usage(void)
161 {
162 	extern char *__progname;
163 	fprintf(stderr, "Usage: %s [-s <size>] -[TDBHS] [-p] [-m]\n",
164 	    __progname);
165 	exit(2);
166 }
167 
168 int
169 main(int argc, char *argv[])
170 {
171 	u_int64_t stack[TESTSZ/8];	/* assuming the testfly() will fit */
172 	struct sigaction sa;
173 	int (*func)(void *, size_t);
174 	size_t size;
175 	char *ep;
176 	void *p, *ptr;
177 	int ch;
178 
179 	if ((page_size = sysconf(_SC_PAGESIZE)) < 0)
180 		err(1, "sysconf");
181 
182 	setvbuf(stdout, NULL, _IONBF, 0);
183 	setvbuf(stderr, NULL, _IONBF, 0);
184 
185 	p = NULL;
186 	func = &noexec;
187 	size = TESTSZ;
188 	while ((ch = getopt(argc, argv, "TDBHSmps:")) != -1) {
189 		if (p == NULL) {
190 			switch (ch) {
191 			case 'T':
192 				p = &testfly;
193 				(void) strlcat(label, "text", sizeof(label));
194 				continue;
195 			case 'D':
196 				p = &data[(PAD + page_size) / 8];
197 				p = (void *)((long)p & ~(page_size - 1));
198 				(void) strlcat(label, "data", sizeof(label));
199 				continue;
200 			case 'B':
201 				p = &bss[(PAD + page_size) / 8];
202 				p = (void *)((long)p & ~(page_size - 1));
203 				(void) strlcat(label, "bss", sizeof(label));
204 				continue;
205 			case 'H':
206 				p = malloc(size + 2 * page_size);
207 				if (p == NULL)
208 					err(2, "malloc");
209 				p += page_size;
210 				p = (void *)((long)p & ~(page_size - 1));
211 				(void) strlcat(label, "heap", sizeof(label));
212 				continue;
213 			case 'S':
214 				p = getaddr(&stack);
215 				(void) strlcat(label, "stack", sizeof(label));
216 				continue;
217 			case 's':	/* only valid for heap and size */
218 				size = strtoul(optarg, &ep, 0);
219 				if (size > ULONG_MAX)
220 					errno = ERANGE;
221 				if (errno)
222 					err(1, "invalid size: %s", optarg);
223 				if (*ep)
224 					errx(1, "invalid size: %s", optarg);
225 				continue;
226 			}
227 		}
228 		switch (ch) {
229 		case 'm':
230 			if (p) {
231 				if ((ptr = mmap(p, size + 2 * page_size,
232 				    PROT_READ|PROT_WRITE,
233 				    MAP_ANON|MAP_FIXED, -1, 0)) == MAP_FAILED)
234 					err(1, "mmap");
235 				(void) strlcat(label, "-mmap", sizeof(label));
236 			} else {
237 				if ((ptr = mmap(p, size + 2 * page_size,
238 				    PROT_READ|PROT_WRITE|PROT_EXEC,
239 				    MAP_ANON, -1, 0)) == MAP_FAILED)
240 					err(1, "mmap");
241 				func = &noexec_mmap;
242 				(void) strlcat(label, "mmap", sizeof(label));
243 			}
244 			p = ptr;
245 			break;
246 		case 'p':
247 			func = &noexec_mprotect;
248 			(void) strlcat(label, "-mprotect", sizeof(label));
249 			break;
250 		default:
251 			usage();
252 		}
253 	}
254 	argc -= optind;
255 	argv += optind;
256 
257 	if (argc > 0)
258 		usage();
259 
260 	if (p == NULL)
261 		exit(2);
262 
263 	sa.sa_sigaction = &sigsegv;
264 	sa.sa_flags = SA_SIGINFO;
265 	sigemptyset(&sa.sa_mask);
266 	sigaction(SIGSEGV, &sa, NULL);
267 
268 	if (p != &testfly) {
269 		memcpy(p, &testfly, TESTSZ);
270 		fdcache(p, size);
271 	}
272 
273 	exit((*func)(p, size));
274 }
275 
276 __asm (".space 8192");
277 
278 void
279 testfly(void)
280 {
281 }
282 
283 __asm (".space 8192");
284