kern_pax.c revision 1.19
1/* $NetBSD: kern_pax.c,v 1.19 2007/12/27 15:21:53 elad Exp $ */ 2 3/*- 4 * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org> 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 BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__KERNEL_RCSID(0, "$NetBSD: kern_pax.c,v 1.19 2007/12/27 15:21:53 elad Exp $"); 32 33#include "opt_pax.h" 34 35#include <sys/param.h> 36#include <sys/proc.h> 37#include <sys/exec_elf.h> 38#include <sys/pax.h> 39#include <sys/sysctl.h> 40#include <sys/malloc.h> 41#include <sys/fileassoc.h> 42#include <sys/syslog.h> 43#include <sys/vnode.h> 44#include <sys/queue.h> 45#include <sys/kauth.h> 46 47#ifdef PAX_ASLR 48#include <sys/mman.h> 49#include <sys/rnd.h> 50#include <sys/exec.h> 51 52int pax_aslr_enabled = 1; 53int pax_aslr_global = PAX_ASLR; 54 55specificdata_key_t pax_aslr_key; 56 57#ifndef PAX_ASLR_DELTA_MMAP_LSB 58#define PAX_ASLR_DELTA_MMAP_LSB PGSHIFT 59#endif 60#ifndef PAX_ASLR_DELTA_MMAP_LEN 61#define PAX_ASLR_DELTA_MMAP_LEN ((sizeof(void *) * NBBY) / 2) 62#endif 63#ifndef PAX_ASLR_DELTA_STACK_LSB 64#define PAX_ASLR_DELTA_STACK_LSB PGSHIFT 65#endif 66#ifndef PAX_ASLR_DELTA_STACK_LEN 67#define PAX_ASLR_DELTA_STACK_LEN 12 68#endif 69 70#endif /* PAX_ASLR */ 71 72#ifdef PAX_MPROTECT 73static int pax_mprotect_enabled = 1; 74static int pax_mprotect_global = PAX_MPROTECT; 75 76specificdata_key_t pax_mprotect_key; 77#endif /* PAX_MPROTECT */ 78 79#ifdef PAX_SEGVGUARD 80#ifndef PAX_SEGVGUARD_EXPIRY 81#define PAX_SEGVGUARD_EXPIRY (2 * 60) 82#endif 83 84#ifndef PAX_SEGVGUARD_SUSPENSION 85#define PAX_SEGVGUARD_SUSPENSION (10 * 60) 86#endif 87 88#ifndef PAX_SEGVGUARD_MAXCRASHES 89#define PAX_SEGVGUARD_MAXCRASHES 5 90#endif 91 92static int pax_segvguard_enabled = 1; 93static int pax_segvguard_global = PAX_SEGVGUARD; 94static int pax_segvguard_expiry = PAX_SEGVGUARD_EXPIRY; 95static int pax_segvguard_suspension = PAX_SEGVGUARD_SUSPENSION; 96static int pax_segvguard_maxcrashes = PAX_SEGVGUARD_MAXCRASHES; 97 98static fileassoc_t segvguard_id; 99specificdata_key_t pax_segvguard_key; 100 101struct pax_segvguard_uid_entry { 102 uid_t sue_uid; 103 size_t sue_ncrashes; 104 time_t sue_expiry; 105 time_t sue_suspended; 106 LIST_ENTRY(pax_segvguard_uid_entry) sue_list; 107}; 108 109struct pax_segvguard_entry { 110 LIST_HEAD(, pax_segvguard_uid_entry) segv_uids; 111}; 112 113static void pax_segvguard_cb(void *); 114#endif /* PAX_SEGVGUARD */ 115 116/* PaX internal setspecific flags */ 117#define PAX_MPROTECT_EXPLICIT_ENABLE (void *)0x01 118#define PAX_MPROTECT_EXPLICIT_DISABLE (void *)0x02 119#define PAX_SEGVGUARD_EXPLICIT_ENABLE (void *)0x03 120#define PAX_SEGVGUARD_EXPLICIT_DISABLE (void *)0x04 121#define PAX_ASLR_EXPLICIT_ENABLE (void *)0x05 122#define PAX_ASLR_EXPLICIT_DISABLE (void *)0x06 123 124SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup") 125{ 126 const struct sysctlnode *rnode = NULL, *cnode; 127 128 sysctl_createv(clog, 0, NULL, &rnode, 129 CTLFLAG_PERMANENT, 130 CTLTYPE_NODE, "security", NULL, 131 NULL, 0, NULL, 0, 132 CTL_SECURITY, CTL_EOL); 133 134 sysctl_createv(clog, 0, &rnode, &rnode, 135 CTLFLAG_PERMANENT, 136 CTLTYPE_NODE, "pax", 137 SYSCTL_DESCR("PaX (exploit mitigation) features."), 138 NULL, 0, NULL, 0, 139 CTL_CREATE, CTL_EOL); 140 141 cnode = rnode; 142 143#ifdef PAX_MPROTECT 144 rnode = cnode; 145 sysctl_createv(clog, 0, &rnode, &rnode, 146 CTLFLAG_PERMANENT, 147 CTLTYPE_NODE, "mprotect", 148 SYSCTL_DESCR("mprotect(2) W^X restrictions."), 149 NULL, 0, NULL, 0, 150 CTL_CREATE, CTL_EOL); 151 sysctl_createv(clog, 0, &rnode, NULL, 152 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 153 CTLTYPE_INT, "enabled", 154 SYSCTL_DESCR("Restrictions enabled."), 155 NULL, 0, &pax_mprotect_enabled, 0, 156 CTL_CREATE, CTL_EOL); 157 sysctl_createv(clog, 0, &rnode, NULL, 158 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 159 CTLTYPE_INT, "global", 160 SYSCTL_DESCR("When enabled, unless explicitly " 161 "specified, apply restrictions to " 162 "all processes."), 163 NULL, 0, &pax_mprotect_global, 0, 164 CTL_CREATE, CTL_EOL); 165#endif /* PAX_MPROTECT */ 166 167#ifdef PAX_SEGVGUARD 168 rnode = cnode; 169 sysctl_createv(clog, 0, &rnode, &rnode, 170 CTLFLAG_PERMANENT, 171 CTLTYPE_NODE, "segvguard", 172 SYSCTL_DESCR("PaX segvguard."), 173 NULL, 0, NULL, 0, 174 CTL_CREATE, CTL_EOL); 175 sysctl_createv(clog, 0, &rnode, NULL, 176 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 177 CTLTYPE_INT, "enabled", 178 SYSCTL_DESCR("segvguard enabled."), 179 NULL, 0, &pax_segvguard_enabled, 0, 180 CTL_CREATE, CTL_EOL); 181 sysctl_createv(clog, 0, &rnode, NULL, 182 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 183 CTLTYPE_INT, "global", 184 SYSCTL_DESCR("segvguard all programs."), 185 NULL, 0, &pax_segvguard_global, 0, 186 CTL_CREATE, CTL_EOL); 187 sysctl_createv(clog, 0, &rnode, NULL, 188 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 189 CTLTYPE_INT, "expiry_timeout", 190 SYSCTL_DESCR("Entry expiry timeout (in seconds)."), 191 NULL, 0, &pax_segvguard_expiry, 0, 192 CTL_CREATE, CTL_EOL); 193 sysctl_createv(clog, 0, &rnode, NULL, 194 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 195 CTLTYPE_INT, "suspend_timeout", 196 SYSCTL_DESCR("Entry suspension timeout (in seconds)."), 197 NULL, 0, &pax_segvguard_suspension, 0, 198 CTL_CREATE, CTL_EOL); 199 sysctl_createv(clog, 0, &rnode, NULL, 200 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 201 CTLTYPE_INT, "max_crashes", 202 SYSCTL_DESCR("Max number of crashes before expiry."), 203 NULL, 0, &pax_segvguard_maxcrashes, 0, 204 CTL_CREATE, CTL_EOL); 205#endif /* PAX_SEGVGUARD */ 206 207#ifdef PAX_ASLR 208 rnode = cnode; 209 sysctl_createv(clog, 0, &rnode, &rnode, 210 CTLFLAG_PERMANENT, 211 CTLTYPE_NODE, "aslr", 212 SYSCTL_DESCR("Address Space Layout Randomization."), 213 NULL, 0, NULL, 0, 214 CTL_CREATE, CTL_EOL); 215 sysctl_createv(clog, 0, &rnode, NULL, 216 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 217 CTLTYPE_INT, "enabled", 218 SYSCTL_DESCR("Restrictions enabled."), 219 NULL, 0, &pax_aslr_enabled, 0, 220 CTL_CREATE, CTL_EOL); 221 sysctl_createv(clog, 0, &rnode, NULL, 222 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 223 CTLTYPE_INT, "global", 224 SYSCTL_DESCR("When enabled, unless explicitly " 225 "specified, apply to all processes."), 226 NULL, 0, &pax_aslr_global, 0, 227 CTL_CREATE, CTL_EOL); 228 sysctl_createv(clog, 0, &rnode, NULL, 229 CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE, 230 CTLTYPE_INT, "mmap_len", 231 SYSCTL_DESCR("Number of bits randomized for " 232 "mmap(2) calls."), 233 NULL, PAX_ASLR_DELTA_MMAP_LEN, NULL, 0, 234 CTL_CREATE, CTL_EOL); 235 sysctl_createv(clog, 0, &rnode, NULL, 236 CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE, 237 CTLTYPE_INT, "stack_len", 238 SYSCTL_DESCR("Number of bits randomized for " 239 "the stack."), 240 NULL, PAX_ASLR_DELTA_STACK_LEN, NULL, 0, 241 CTL_CREATE, CTL_EOL); 242 sysctl_createv(clog, 0, &rnode, NULL, 243 CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE, 244 CTLTYPE_INT, "exec_len", 245 SYSCTL_DESCR("Number of bits randomized for " 246 "the PIE exec base."), 247 NULL, PAX_ASLR_DELTA_EXEC_LEN, NULL, 0, 248 CTL_CREATE, CTL_EOL); 249 250#endif /* PAX_ASLR */ 251} 252 253/* 254 * Initialize PaX. 255 */ 256void 257pax_init(void) 258{ 259#ifdef PAX_SEGVGUARD 260 int error; 261#endif /* PAX_SEGVGUARD */ 262 263#ifdef PAX_ASLR 264 proc_specific_key_create(&pax_aslr_key, NULL); 265#endif /* PAX_ASLR */ 266 267#ifdef PAX_MPROTECT 268 proc_specific_key_create(&pax_mprotect_key, NULL); 269#endif /* PAX_MPROTECT */ 270 271#ifdef PAX_SEGVGUARD 272 error = fileassoc_register("segvguard", pax_segvguard_cb, 273 &segvguard_id); 274 if (error) { 275 panic("pax_init: segvguard_id: error=%d\n", error); 276 } 277 proc_specific_key_create(&pax_segvguard_key, NULL); 278#endif /* PAX_SEGVGUARD */ 279} 280 281void 282pax_adjust(struct lwp *l, uint32_t f) 283{ 284#ifdef PAX_MPROTECT 285 if (pax_mprotect_enabled) { 286 if (f & ELF_NOTE_PAX_MPROTECT) 287 proc_setspecific(l->l_proc, pax_mprotect_key, 288 PAX_MPROTECT_EXPLICIT_ENABLE); 289 if (f & ELF_NOTE_PAX_NOMPROTECT) 290 proc_setspecific(l->l_proc, pax_mprotect_key, 291 PAX_MPROTECT_EXPLICIT_DISABLE); 292 } 293#endif /* PAX_MPROTECT */ 294 295#ifdef PAX_SEGVGUARD 296 if (pax_segvguard_enabled) { 297 if (f & ELF_NOTE_PAX_GUARD) 298 proc_setspecific(l->l_proc, pax_segvguard_key, 299 PAX_SEGVGUARD_EXPLICIT_ENABLE); 300 if (f & ELF_NOTE_PAX_NOGUARD) 301 proc_setspecific(l->l_proc, pax_segvguard_key, 302 PAX_SEGVGUARD_EXPLICIT_DISABLE); 303 } 304#endif /* PAX_SEGVGUARD */ 305 306#ifdef PAX_ASLR 307 if (pax_aslr_enabled) { 308 if (f & ELF_NOTE_PAX_ASLR) 309 proc_setspecific(l->l_proc, pax_aslr_key, 310 PAX_ASLR_EXPLICIT_ENABLE); 311 if (f & ELF_NOTE_PAX_NOASLR) 312 proc_setspecific(l->l_proc, pax_aslr_key, 313 PAX_ASLR_EXPLICIT_DISABLE); 314 } 315#endif /* PAX_ASLR */ 316} 317 318#ifdef PAX_MPROTECT 319void 320pax_mprotect(struct lwp *l, vm_prot_t *prot, vm_prot_t *maxprot) 321{ 322 void *t; 323 324 if (!pax_mprotect_enabled) 325 return; 326 327 t = proc_getspecific(l->l_proc, pax_mprotect_key); 328 if ((pax_mprotect_global && t == PAX_MPROTECT_EXPLICIT_DISABLE) || 329 (!pax_mprotect_global && t != PAX_MPROTECT_EXPLICIT_ENABLE)) 330 return; 331 332 if ((*prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) != VM_PROT_EXECUTE) { 333 *prot &= ~VM_PROT_EXECUTE; 334 *maxprot &= ~VM_PROT_EXECUTE; 335 } else { 336 *prot &= ~VM_PROT_WRITE; 337 *maxprot &= ~VM_PROT_WRITE; 338 } 339} 340#endif /* PAX_MPROTECT */ 341 342#ifdef PAX_ASLR 343bool 344pax_aslr_active(struct lwp *l) 345{ 346 void *t; 347 if (!pax_aslr_enabled) 348 return false; 349 350 t = proc_getspecific(l->l_proc, pax_aslr_key); 351 if ((pax_aslr_global && t == PAX_ASLR_EXPLICIT_DISABLE) || 352 (!pax_aslr_global && t != PAX_ASLR_EXPLICIT_ENABLE)) 353 return false; 354 return true; 355} 356 357void 358pax_aslr_init(struct lwp *l, struct vmspace *vm) 359{ 360 if (!pax_aslr_active(l)) 361 return; 362 363 vm->vm_aslr_delta_mmap = PAX_ASLR_DELTA(arc4random(), 364 PAX_ASLR_DELTA_MMAP_LSB, PAX_ASLR_DELTA_MMAP_LEN); 365} 366 367void 368pax_aslr(struct lwp *l, vaddr_t *addr, vaddr_t orig_addr, int f) 369{ 370 if (!pax_aslr_active(l)) 371 return; 372 373 if (!(f & MAP_FIXED) && ((orig_addr == 0) || !(f & MAP_ANON))) { 374#ifdef DEBUG_ASLR 375 uprintf("applying to 0x%lx orig_addr=0x%lx f=%x\n", 376 (unsigned long)*addr, (unsigned long)orig_addr, f); 377#endif 378 if (!(l->l_proc->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN)) 379 *addr += l->l_proc->p_vmspace->vm_aslr_delta_mmap; 380 else 381 *addr -= l->l_proc->p_vmspace->vm_aslr_delta_mmap; 382#ifdef DEBUG_ASLR 383 uprintf("result 0x%lx\n", *addr); 384#endif 385 } 386#ifdef DEBUG_ASLR 387 else 388 uprintf("not applying to 0x%lx orig_addr=0x%lx f=%x\n", 389 (unsigned long)*addr, (unsigned long)orig_addr, f); 390#endif 391} 392 393void 394pax_aslr_stack(struct lwp *l, struct exec_package *epp, u_long *max_stack_size) 395{ 396 if (pax_aslr_active(l)) { 397 u_long d = PAX_ASLR_DELTA(epp->ep_random, 398 PAX_ASLR_DELTA_STACK_LSB, 399 PAX_ASLR_DELTA_STACK_LEN); 400#ifdef DEBUG_ASLR 401 uprintf("stack 0x%lx d=0x%lx 0x%lx\n", 402 epp->ep_minsaddr, d, epp->ep_minsaddr - d); 403#endif 404 epp->ep_minsaddr -= d; 405 *max_stack_size -= d; 406 } 407} 408#endif /* PAX_ASLR */ 409 410#ifdef PAX_SEGVGUARD 411static void 412pax_segvguard_cb(void *v) 413{ 414 struct pax_segvguard_entry *p; 415 struct pax_segvguard_uid_entry *up; 416 417 if (v == NULL) 418 return; 419 420 p = v; 421 while ((up = LIST_FIRST(&p->segv_uids)) != NULL) { 422 LIST_REMOVE(up, sue_list); 423 free(up, M_TEMP); 424 } 425 426 free(v, M_TEMP); 427} 428 429/* 430 * Called when a process of image vp generated a segfault. 431 */ 432int 433pax_segvguard(struct lwp *l, struct vnode *vp, const char *name, 434 bool crashed) 435{ 436 struct pax_segvguard_entry *p; 437 struct pax_segvguard_uid_entry *up; 438 struct timeval tv; 439 uid_t uid; 440 void *t; 441 bool have_uid; 442 443 if (!pax_segvguard_enabled) 444 return (0); 445 446 t = proc_getspecific(l->l_proc, pax_segvguard_key); 447 if ((pax_segvguard_global && t == PAX_SEGVGUARD_EXPLICIT_DISABLE) || 448 (!pax_segvguard_global && t != PAX_SEGVGUARD_EXPLICIT_ENABLE)) 449 return (0); 450 451 if (vp == NULL) 452 return (EFAULT); 453 454 /* Check if we already monitor the file. */ 455 p = fileassoc_lookup(vp, segvguard_id); 456 457 /* Fast-path if starting a program we don't know. */ 458 if (p == NULL && !crashed) 459 return (0); 460 461 microtime(&tv); 462 463 /* 464 * If a program we don't know crashed, we need to create a new entry 465 * for it. 466 */ 467 if (p == NULL) { 468 p = malloc(sizeof(*p), M_TEMP, M_WAITOK); 469 fileassoc_add(vp, segvguard_id, p); 470 LIST_INIT(&p->segv_uids); 471 472 /* 473 * Initialize a new entry with "crashes so far" of 1. 474 * The expiry time is when we purge the entry if it didn't 475 * reach the limit. 476 */ 477 up = malloc(sizeof(*up), M_TEMP, M_WAITOK); 478 up->sue_uid = kauth_cred_getuid(l->l_cred); 479 up->sue_ncrashes = 1; 480 up->sue_expiry = tv.tv_sec + pax_segvguard_expiry; 481 up->sue_suspended = 0; 482 483 LIST_INSERT_HEAD(&p->segv_uids, up, sue_list); 484 485 return (0); 486 } 487 488 /* 489 * A program we "know" either executed or crashed again. 490 * See if it's a culprit we're familiar with. 491 */ 492 uid = kauth_cred_getuid(l->l_cred); 493 have_uid = false; 494 LIST_FOREACH(up, &p->segv_uids, sue_list) { 495 if (up->sue_uid == uid) { 496 have_uid = true; 497 break; 498 } 499 } 500 501 /* 502 * It's someone else. Add an entry for him if we crashed. 503 */ 504 if (!have_uid) { 505 if (crashed) { 506 up = malloc(sizeof(*up), M_TEMP, M_WAITOK); 507 up->sue_uid = uid; 508 up->sue_ncrashes = 1; 509 up->sue_expiry = tv.tv_sec + pax_segvguard_expiry; 510 up->sue_suspended = 0; 511 512 LIST_INSERT_HEAD(&p->segv_uids, up, sue_list); 513 } 514 515 return (0); 516 } 517 518 if (crashed) { 519 /* Check if timer on previous crashes expired first. */ 520 if (up->sue_expiry < tv.tv_sec) { 521 log(LOG_INFO, "PaX Segvguard: [%s] Suspension" 522 " expired.\n", name ? name : "unknown"); 523 524 up->sue_ncrashes = 1; 525 up->sue_expiry = tv.tv_sec + pax_segvguard_expiry; 526 up->sue_suspended = 0; 527 528 return (0); 529 } 530 531 up->sue_ncrashes++; 532 533 if (up->sue_ncrashes >= pax_segvguard_maxcrashes) { 534 log(LOG_ALERT, "PaX Segvguard: [%s] Suspending " 535 "execution for %d seconds after %zu crashes.\n", 536 name ? name : "unknown", pax_segvguard_suspension, 537 up->sue_ncrashes); 538 539 /* Suspend this program for a while. */ 540 up->sue_suspended = tv.tv_sec + pax_segvguard_suspension; 541 up->sue_ncrashes = 0; 542 up->sue_expiry = 0; 543 } 544 } else { 545 /* Are we supposed to be suspended? */ 546 if (up->sue_suspended > tv.tv_sec) { 547 log(LOG_ALERT, "PaX Segvguard: [%s] Preventing " 548 "execution due to repeated segfaults.\n", name ? 549 name : "unknown"); 550 551 return (EPERM); 552 } 553 } 554 555 return (0); 556} 557#endif /* PAX_SEGVGUARD */ 558