1 /* $OpenBSD: noexec.c,v 1.15 2014/04/18 14:38:21 guenther 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 #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 if ((void *)&ret < a) 124 ret = (void *)((u_long)&ret - 4 * page_size); 125 else 126 ret = (void *)((u_long)&ret + 4 * page_size); 127 128 return (void *)((u_long)ret & ~(page_size - 1)); 129 } 130 131 static int 132 noexec_mmap(void *p, size_t size) 133 { 134 memcpy(p + page_size * 1, p, page_size); 135 memcpy(p + page_size * 2, p, page_size); 136 fdcache(p + page_size * 1, TESTSZ); 137 fdcache(p + page_size * 2, TESTSZ); 138 139 /* here we must fail on segv since we said it gets executable */ 140 fail = 1; 141 142 printf("%s: execute #1\n", label); 143 fflush(stdout); 144 ((void (*)(void))p)(); 145 146 /* unmap the first page to see that the higher page is still exec */ 147 if (munmap(p, page_size) < 0) 148 err(1, "munmap"); 149 150 p += page_size; 151 printf("%s: execute #2\n", label); 152 fflush(stdout); 153 ((void (*)(void))p)(); 154 155 /* unmap the last page to see that the lower page is still exec */ 156 if (munmap(p + page_size, page_size) < 0) 157 err(1, "munmap"); 158 159 printf("%s: execute #3\n", label); 160 fflush(stdout); 161 ((void (*)(void))p)(); 162 163 return (0); 164 } 165 166 static void 167 usage(void) 168 { 169 extern char *__progname; 170 fprintf(stderr, "Usage: %s [-s <size>] -[TDBHS] [-p] [-m]\n", 171 __progname); 172 exit(2); 173 } 174 175 int 176 main(int argc, char *argv[]) 177 { 178 u_int64_t stack[TESTSZ/8]; /* assuming the testfly() will fit */ 179 struct sigaction sa; 180 int (*func)(void *, size_t); 181 size_t size; 182 char *ep; 183 void *p, *ptr; 184 int ch; 185 186 if ((page_size = sysconf(_SC_PAGESIZE)) < 0) 187 err(1, "sysconf"); 188 189 setvbuf(stdout, NULL, _IONBF, 0); 190 setvbuf(stderr, NULL, _IONBF, 0); 191 192 p = NULL; 193 func = &noexec; 194 size = TESTSZ; 195 while ((ch = getopt(argc, argv, "TDBHSmps:")) != -1) { 196 if (p == NULL) { 197 switch (ch) { 198 case 'T': 199 p = &testfly; 200 (void) strlcat(label, "text", sizeof(label)); 201 continue; 202 case 'D': 203 p = &data[(PAD + page_size) / 8]; 204 p = (void *)((long)p & ~(page_size - 1)); 205 (void) strlcat(label, "data", sizeof(label)); 206 continue; 207 case 'B': 208 p = &bss[(PAD + page_size) / 8]; 209 p = (void *)((long)p & ~(page_size - 1)); 210 (void) strlcat(label, "bss", sizeof(label)); 211 continue; 212 case 'H': 213 p = malloc(size + 2 * page_size); 214 if (p == NULL) 215 err(2, "malloc"); 216 p += page_size; 217 p = (void *)((long)p & ~(page_size - 1)); 218 (void) strlcat(label, "heap", sizeof(label)); 219 continue; 220 case 'S': 221 p = getaddr(&stack); 222 (void) strlcat(label, "stack", sizeof(label)); 223 continue; 224 case 's': /* only valid for heap and size */ 225 size = strtoul(optarg, &ep, 0); 226 if (size > ULONG_MAX) 227 errno = ERANGE; 228 if (errno) 229 err(1, "invalid size: %s", optarg); 230 if (*ep) 231 errx(1, "invalid size: %s", optarg); 232 continue; 233 } 234 } 235 switch (ch) { 236 case 'm': 237 if (p) { 238 if ((ptr = mmap(p, size + 2 * page_size, 239 PROT_READ|PROT_WRITE, 240 MAP_ANON|MAP_FIXED, -1, 0)) == MAP_FAILED) 241 err(1, "mmap"); 242 (void) strlcat(label, "-mmap", sizeof(label)); 243 } else { 244 if ((ptr = mmap(p, size + 2 * page_size, 245 PROT_READ|PROT_WRITE|PROT_EXEC, 246 MAP_ANON, -1, 0)) == MAP_FAILED) 247 err(1, "mmap"); 248 func = &noexec_mmap; 249 (void) strlcat(label, "mmap", sizeof(label)); 250 } 251 p = ptr; 252 break; 253 case 'p': 254 func = &noexec_mprotect; 255 (void) strlcat(label, "-mprotect", sizeof(label)); 256 break; 257 default: 258 usage(); 259 } 260 } 261 argc -= optind; 262 argv += optind; 263 264 if (argc > 0) 265 usage(); 266 267 if (p == NULL) 268 exit(2); 269 270 sa.sa_sigaction = &sigsegv; 271 sa.sa_flags = SA_SIGINFO; 272 sigemptyset(&sa.sa_mask); 273 sigaction(SIGSEGV, &sa, NULL); 274 275 if (p != &testfly) { 276 memcpy(p, &testfly, TESTSZ); 277 fdcache(p, size); 278 } 279 280 exit((*func)(p, size)); 281 } 282