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