Home | History | Annotate | Line # | Download | only in kern
kern_pax.c revision 1.34
      1 /*	$NetBSD: kern_pax.c,v 1.34 2016/03/19 18:56:37 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2015 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Maxime Villard.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * Copyright (c) 2006 Elad Efrat <elad (at) NetBSD.org>
     34  * All rights reserved.
     35  *
     36  * Redistribution and use in source and binary forms, with or without
     37  * modification, are permitted provided that the following conditions
     38  * are met:
     39  * 1. Redistributions of source code must retain the above copyright
     40  *    notice, this list of conditions and the following disclaimer.
     41  * 2. Redistributions in binary form must reproduce the above copyright
     42  *    notice, this list of conditions and the following disclaimer in the
     43  *    documentation and/or other materials provided with the distribution.
     44  * 3. The name of the author may not be used to endorse or promote products
     45  *    derived from this software without specific prior written permission.
     46  *
     47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     48  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     49  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     50  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     51  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     52  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     53  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     54  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     55  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     56  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     57  */
     58 
     59 #include <sys/cdefs.h>
     60 __KERNEL_RCSID(0, "$NetBSD: kern_pax.c,v 1.34 2016/03/19 18:56:37 christos Exp $");
     61 
     62 #include "opt_pax.h"
     63 
     64 #include <sys/param.h>
     65 #include <sys/proc.h>
     66 #include <sys/exec.h>
     67 #include <sys/exec_elf.h>
     68 #include <sys/pax.h>
     69 #include <sys/sysctl.h>
     70 #include <sys/kmem.h>
     71 #include <sys/mman.h>
     72 #include <sys/fileassoc.h>
     73 #include <sys/syslog.h>
     74 #include <sys/vnode.h>
     75 #include <sys/queue.h>
     76 #include <sys/kauth.h>
     77 #include <sys/cprng.h>
     78 
     79 #ifdef PAX_ASLR_DEBUG
     80 #define PAX_DPRINTF(_fmt, args...) \
     81 	do if (pax_aslr_debug) uprintf("%s: " _fmt "\n", __func__, ##args); \
     82 	while (/*CONSTCOND*/0)
     83 #else
     84 #define PAX_DPRINTF(_fmt, args...)	do {} while (/*CONSTCOND*/0)
     85 #endif
     86 
     87 #ifdef PAX_ASLR
     88 #include <sys/mman.h>
     89 
     90 int pax_aslr_enabled = 1;
     91 int pax_aslr_global = PAX_ASLR;
     92 
     93 #ifndef PAX_ASLR_DELTA_MMAP_LSB
     94 #define PAX_ASLR_DELTA_MMAP_LSB		PGSHIFT
     95 #endif
     96 #ifndef PAX_ASLR_DELTA_MMAP_LEN
     97 #define PAX_ASLR_DELTA_MMAP_LEN		((sizeof(void *) * NBBY) / 2)
     98 #endif
     99 #ifndef PAX_ASLR_DELTA_STACK_LSB
    100 #define PAX_ASLR_DELTA_STACK_LSB	PGSHIFT
    101 #endif
    102 #ifndef PAX_ASLR_DELTA_STACK_LEN
    103 #define PAX_ASLR_DELTA_STACK_LEN 	12
    104 #endif
    105 
    106 static bool pax_aslr_elf_flags_active(uint32_t);
    107 #endif /* PAX_ASLR */
    108 
    109 #ifdef PAX_MPROTECT
    110 static int pax_mprotect_enabled = 1;
    111 static int pax_mprotect_global = PAX_MPROTECT;
    112 static bool pax_mprotect_elf_flags_active(uint32_t);
    113 #endif /* PAX_MPROTECT */
    114 
    115 #ifdef PAX_SEGVGUARD
    116 #ifndef PAX_SEGVGUARD_EXPIRY
    117 #define	PAX_SEGVGUARD_EXPIRY		(2 * 60)
    118 #endif
    119 #ifndef PAX_SEGVGUARD_SUSPENSION
    120 #define	PAX_SEGVGUARD_SUSPENSION	(10 * 60)
    121 #endif
    122 #ifndef	PAX_SEGVGUARD_MAXCRASHES
    123 #define	PAX_SEGVGUARD_MAXCRASHES	5
    124 #endif
    125 
    126 #ifdef PAX_ASLR_DEBUG
    127 int pax_aslr_debug;
    128 #endif
    129 
    130 static int pax_segvguard_enabled = 1;
    131 static int pax_segvguard_global = PAX_SEGVGUARD;
    132 static int pax_segvguard_expiry = PAX_SEGVGUARD_EXPIRY;
    133 static int pax_segvguard_suspension = PAX_SEGVGUARD_SUSPENSION;
    134 static int pax_segvguard_maxcrashes = PAX_SEGVGUARD_MAXCRASHES;
    135 
    136 static fileassoc_t segvguard_id;
    137 
    138 struct pax_segvguard_uid_entry {
    139 	uid_t sue_uid;
    140 	size_t sue_ncrashes;
    141 	time_t sue_expiry;
    142 	time_t sue_suspended;
    143 	LIST_ENTRY(pax_segvguard_uid_entry) sue_list;
    144 };
    145 
    146 struct pax_segvguard_entry {
    147 	LIST_HEAD(, pax_segvguard_uid_entry) segv_uids;
    148 };
    149 
    150 static bool pax_segvguard_elf_flags_active(uint32_t);
    151 static void pax_segvguard_cleanup_cb(void *);
    152 #endif /* PAX_SEGVGUARD */
    153 
    154 SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup")
    155 {
    156 	const struct sysctlnode *rnode = NULL, *cnode;
    157 
    158 	sysctl_createv(clog, 0, NULL, &rnode,
    159 		       CTLFLAG_PERMANENT,
    160 		       CTLTYPE_NODE, "pax",
    161 		       SYSCTL_DESCR("PaX (exploit mitigation) features."),
    162 		       NULL, 0, NULL, 0,
    163 		       CTL_SECURITY, CTL_CREATE, CTL_EOL);
    164 
    165 	cnode = rnode;
    166 
    167 #ifdef PAX_MPROTECT
    168 	rnode = cnode;
    169 	sysctl_createv(clog, 0, &rnode, &rnode,
    170 		       CTLFLAG_PERMANENT,
    171 		       CTLTYPE_NODE, "mprotect",
    172 		       SYSCTL_DESCR("mprotect(2) W^X restrictions."),
    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("Restrictions enabled."),
    179 		       NULL, 0, &pax_mprotect_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("When enabled, unless explicitly "
    185 				    "specified, apply restrictions to "
    186 				    "all processes."),
    187 		       NULL, 0, &pax_mprotect_global, 0,
    188 		       CTL_CREATE, CTL_EOL);
    189 #endif /* PAX_MPROTECT */
    190 
    191 #ifdef PAX_SEGVGUARD
    192 	rnode = cnode;
    193 	sysctl_createv(clog, 0, &rnode, &rnode,
    194 		       CTLFLAG_PERMANENT,
    195 		       CTLTYPE_NODE, "segvguard",
    196 		       SYSCTL_DESCR("PaX segvguard."),
    197 		       NULL, 0, NULL, 0,
    198 		       CTL_CREATE, CTL_EOL);
    199 	sysctl_createv(clog, 0, &rnode, NULL,
    200 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    201 		       CTLTYPE_INT, "enabled",
    202 		       SYSCTL_DESCR("segvguard enabled."),
    203 		       NULL, 0, &pax_segvguard_enabled, 0,
    204 		       CTL_CREATE, CTL_EOL);
    205 	sysctl_createv(clog, 0, &rnode, NULL,
    206 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    207 		       CTLTYPE_INT, "global",
    208 		       SYSCTL_DESCR("segvguard all programs."),
    209 		       NULL, 0, &pax_segvguard_global, 0,
    210 		       CTL_CREATE, CTL_EOL);
    211 	sysctl_createv(clog, 0, &rnode, NULL,
    212 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    213 		       CTLTYPE_INT, "expiry_timeout",
    214 		       SYSCTL_DESCR("Entry expiry timeout (in seconds)."),
    215 		       NULL, 0, &pax_segvguard_expiry, 0,
    216 		       CTL_CREATE, CTL_EOL);
    217 	sysctl_createv(clog, 0, &rnode, NULL,
    218 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    219 		       CTLTYPE_INT, "suspend_timeout",
    220 		       SYSCTL_DESCR("Entry suspension timeout (in seconds)."),
    221 		       NULL, 0, &pax_segvguard_suspension, 0,
    222 		       CTL_CREATE, CTL_EOL);
    223 	sysctl_createv(clog, 0, &rnode, NULL,
    224 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    225 		       CTLTYPE_INT, "max_crashes",
    226 		       SYSCTL_DESCR("Max number of crashes before expiry."),
    227 		       NULL, 0, &pax_segvguard_maxcrashes, 0,
    228 		       CTL_CREATE, CTL_EOL);
    229 #endif /* PAX_SEGVGUARD */
    230 
    231 #ifdef PAX_ASLR
    232 	rnode = cnode;
    233 	sysctl_createv(clog, 0, &rnode, &rnode,
    234 		       CTLFLAG_PERMANENT,
    235 		       CTLTYPE_NODE, "aslr",
    236 		       SYSCTL_DESCR("Address Space Layout Randomization."),
    237 		       NULL, 0, NULL, 0,
    238 		       CTL_CREATE, CTL_EOL);
    239 	sysctl_createv(clog, 0, &rnode, NULL,
    240 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    241 		       CTLTYPE_INT, "enabled",
    242 		       SYSCTL_DESCR("Restrictions enabled."),
    243 		       NULL, 0, &pax_aslr_enabled, 0,
    244 		       CTL_CREATE, CTL_EOL);
    245 	sysctl_createv(clog, 0, &rnode, NULL,
    246 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    247 		       CTLTYPE_INT, "global",
    248 		       SYSCTL_DESCR("When enabled, unless explicitly "
    249 				    "specified, apply to all processes."),
    250 		       NULL, 0, &pax_aslr_global, 0,
    251 		       CTL_CREATE, CTL_EOL);
    252 #ifdef PAX_ASLR_DEBUG
    253 	sysctl_createv(clog, 0, &rnode, NULL,
    254 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    255 		       CTLTYPE_INT, "debug",
    256 		       SYSCTL_DESCR("Pring ASLR selected addresses."),
    257 		       NULL, 0, &pax_aslr_debug, 0,
    258 		       CTL_CREATE, CTL_EOL);
    259 #endif
    260 	sysctl_createv(clog, 0, &rnode, NULL,
    261 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
    262 		       CTLTYPE_INT, "mmap_len",
    263 		       SYSCTL_DESCR("Number of bits randomized for "
    264 				    "mmap(2) calls."),
    265 		       NULL, PAX_ASLR_DELTA_MMAP_LEN, NULL, 0,
    266 		       CTL_CREATE, CTL_EOL);
    267 	sysctl_createv(clog, 0, &rnode, NULL,
    268 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
    269 		       CTLTYPE_INT, "stack_len",
    270 		       SYSCTL_DESCR("Number of bits randomized for "
    271 				    "the stack."),
    272 		       NULL, PAX_ASLR_DELTA_STACK_LEN, NULL, 0,
    273 		       CTL_CREATE, CTL_EOL);
    274 	sysctl_createv(clog, 0, &rnode, NULL,
    275 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
    276 		       CTLTYPE_INT, "exec_len",
    277 		       SYSCTL_DESCR("Number of bits randomized for "
    278 				    "the PIE exec base."),
    279 		       NULL, PAX_ASLR_DELTA_EXEC_LEN, NULL, 0,
    280 		       CTL_CREATE, CTL_EOL);
    281 
    282 #endif /* PAX_ASLR */
    283 }
    284 
    285 /*
    286  * Initialize PaX.
    287  */
    288 void
    289 pax_init(void)
    290 {
    291 #ifdef PAX_SEGVGUARD
    292 	int error;
    293 
    294 	error = fileassoc_register("segvguard", pax_segvguard_cleanup_cb,
    295 	    &segvguard_id);
    296 	if (error) {
    297 		panic("pax_init: segvguard_id: error=%d\n", error);
    298 	}
    299 #endif /* PAX_SEGVGUARD */
    300 }
    301 
    302 void
    303 pax_setup_elf_flags(struct exec_package *epp, uint32_t elf_flags)
    304 {
    305 	uint32_t flags = 0;
    306 
    307 #ifdef PAX_ASLR
    308 	if (pax_aslr_elf_flags_active(elf_flags)) {
    309 		flags |= P_PAX_ASLR;
    310 	}
    311 #endif
    312 #ifdef PAX_MPROTECT
    313 	if (pax_mprotect_elf_flags_active(elf_flags)) {
    314 		flags |= P_PAX_MPROTECT;
    315 	}
    316 #endif
    317 #ifdef PAX_SEGVGUARD
    318 	if (pax_segvguard_elf_flags_active(elf_flags)) {
    319 		flags |= P_PAX_GUARD;
    320 	}
    321 #endif
    322 
    323 	epp->ep_pax_flags = flags;
    324 }
    325 
    326 #if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD) || defined(PAX_ASLR)
    327 static inline bool
    328 pax_flags_active(uint32_t flags, uint32_t opt)
    329 {
    330 	if (!(flags & opt))
    331 		return false;
    332 	return true;
    333 }
    334 #endif /* PAX_MPROTECT || PAX_SEGVGUARD || PAX_ASLR */
    335 
    336 #ifdef PAX_MPROTECT
    337 static bool
    338 pax_mprotect_elf_flags_active(uint32_t flags)
    339 {
    340 	if (!pax_mprotect_enabled)
    341 		return false;
    342 	if (pax_mprotect_global && (flags & ELF_NOTE_PAX_NOMPROTECT) != 0) {
    343 		/* Mprotect explicitly disabled */
    344 		return false;
    345 	}
    346 	if (!pax_mprotect_global && (flags & ELF_NOTE_PAX_MPROTECT) == 0) {
    347 		/* Mprotect not requested */
    348 		return false;
    349 	}
    350 	return true;
    351 }
    352 
    353 void
    354 pax_mprotect(struct lwp *l, vm_prot_t *prot, vm_prot_t *maxprot)
    355 {
    356 	uint32_t flags;
    357 
    358 	flags = l->l_proc->p_pax;
    359 	if (!pax_flags_active(flags, P_PAX_MPROTECT))
    360 		return;
    361 
    362 	if ((*prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) != VM_PROT_EXECUTE) {
    363 		*prot &= ~VM_PROT_EXECUTE;
    364 		*maxprot &= ~VM_PROT_EXECUTE;
    365 	} else {
    366 		*prot &= ~VM_PROT_WRITE;
    367 		*maxprot &= ~VM_PROT_WRITE;
    368 	}
    369 }
    370 #endif /* PAX_MPROTECT */
    371 
    372 #ifdef PAX_ASLR
    373 static bool
    374 pax_aslr_elf_flags_active(uint32_t flags)
    375 {
    376 	if (!pax_aslr_enabled)
    377 		return false;
    378 	if (pax_aslr_global && (flags & ELF_NOTE_PAX_NOASLR) != 0) {
    379 		/* ASLR explicitly disabled */
    380 		return false;
    381 	}
    382 	if (!pax_aslr_global && (flags & ELF_NOTE_PAX_ASLR) == 0) {
    383 		/* ASLR not requested */
    384 		return false;
    385 	}
    386 	return true;
    387 }
    388 
    389 bool
    390 pax_aslr_epp_active(struct exec_package *epp)
    391 {
    392 	return pax_flags_active(epp->ep_pax_flags, P_PAX_ASLR);
    393 }
    394 
    395 bool
    396 pax_aslr_active(struct lwp *l)
    397 {
    398 	return pax_flags_active(l->l_proc->p_pax, P_PAX_ASLR);
    399 }
    400 
    401 void
    402 pax_aslr_init_vm(struct lwp *l, struct vmspace *vm)
    403 {
    404 	if (!pax_aslr_active(l))
    405 		return;
    406 
    407 	vm->vm_aslr_delta_mmap = PAX_ASLR_DELTA(cprng_fast32(),
    408 	    PAX_ASLR_DELTA_MMAP_LSB, PAX_ASLR_DELTA_MMAP_LEN);
    409 	PAX_DPRINTF("delta_mmap=%#jx", vm->vm_aslr_delta_mmap);
    410 }
    411 
    412 void
    413 pax_aslr_mmap(struct lwp *l, vaddr_t *addr, vaddr_t orig_addr, int f)
    414 {
    415 	if (!pax_aslr_active(l))
    416 		return;
    417 #ifdef PAX_ASLR_DEBUG
    418 	char buf[256];
    419 	if (pax_aslr_debug)
    420 		snprintb(buf, sizeof(buf), MAP_FMT, f);
    421 	else
    422 		buf[0] = '\0';
    423 #endif
    424 
    425 	if (!(f & MAP_FIXED) && ((orig_addr == 0) || !(f & MAP_ANON))) {
    426 		PAX_DPRINTF("applying to %#jx orig_addr=%#jx f=%s",
    427 		    (uintmax_t)*addr, (uintmax_t)orig_addr, buf);
    428 		if (!(l->l_proc->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN))
    429 			*addr += l->l_proc->p_vmspace->vm_aslr_delta_mmap;
    430 		else
    431 			*addr -= l->l_proc->p_vmspace->vm_aslr_delta_mmap;
    432 		PAX_DPRINTF("result %#jx", (uintmax_t)*addr);
    433 	} else {
    434 		PAX_DPRINTF("not applying to %#jx orig_addr=%#jx f=%s",
    435 		    (uintmax_t)*addr, (uintmax_t)orig_addr, buf);
    436 	}
    437 }
    438 
    439 void
    440 pax_aslr_stack(struct exec_package *epp, u_long *max_stack_size)
    441 {
    442 	if (!pax_aslr_epp_active(epp))
    443 		return;
    444 
    445 	u_long d = PAX_ASLR_DELTA(cprng_fast32(),
    446 	    PAX_ASLR_DELTA_STACK_LSB,
    447 	    PAX_ASLR_DELTA_STACK_LEN);
    448 	PAX_DPRINTF("stack %#jx delta=%#lx diff=%lx",
    449 	    (uintmax_t)epp->ep_minsaddr, d, epp->ep_minsaddr - d);
    450 	epp->ep_minsaddr -= d;
    451 	*max_stack_size -= d;
    452 	if (epp->ep_ssize > *max_stack_size)
    453 		epp->ep_ssize = *max_stack_size;
    454 }
    455 #endif /* PAX_ASLR */
    456 
    457 #ifdef PAX_SEGVGUARD
    458 static bool
    459 pax_segvguard_elf_flags_active(uint32_t flags)
    460 {
    461 	if (!pax_segvguard_enabled)
    462 		return false;
    463 	if (pax_segvguard_global && (flags & ELF_NOTE_PAX_NOGUARD) != 0) {
    464 		/* Segvguard explicitly disabled */
    465 		return false;
    466 	}
    467 	if (!pax_segvguard_global && (flags & ELF_NOTE_PAX_GUARD) == 0) {
    468 		/* Segvguard not requested */
    469 		return false;
    470 	}
    471 	return true;
    472 }
    473 
    474 static void
    475 pax_segvguard_cleanup_cb(void *v)
    476 {
    477 	struct pax_segvguard_entry *p = v;
    478 	struct pax_segvguard_uid_entry *up;
    479 
    480 	if (p == NULL) {
    481 		return;
    482 	}
    483 	while ((up = LIST_FIRST(&p->segv_uids)) != NULL) {
    484 		LIST_REMOVE(up, sue_list);
    485 		kmem_free(up, sizeof(*up));
    486 	}
    487 	kmem_free(p, sizeof(*p));
    488 }
    489 
    490 /*
    491  * Called when a process of image vp generated a segfault.
    492  */
    493 int
    494 pax_segvguard(struct lwp *l, struct vnode *vp, const char *name,
    495     bool crashed)
    496 {
    497 	struct pax_segvguard_entry *p;
    498 	struct pax_segvguard_uid_entry *up;
    499 	struct timeval tv;
    500 	uid_t uid;
    501 	uint32_t flags;
    502 	bool have_uid;
    503 
    504 	flags = l->l_proc->p_pax;
    505 	if (!pax_flags_active(flags, P_PAX_GUARD))
    506 		return 0;
    507 
    508 	if (vp == NULL)
    509 		return EFAULT;
    510 
    511 	/* Check if we already monitor the file. */
    512 	p = fileassoc_lookup(vp, segvguard_id);
    513 
    514 	/* Fast-path if starting a program we don't know. */
    515 	if (p == NULL && !crashed)
    516 		return 0;
    517 
    518 	microtime(&tv);
    519 
    520 	/*
    521 	 * If a program we don't know crashed, we need to create a new entry
    522 	 * for it.
    523 	 */
    524 	if (p == NULL) {
    525 		p = kmem_alloc(sizeof(*p), KM_SLEEP);
    526 		fileassoc_add(vp, segvguard_id, p);
    527 		LIST_INIT(&p->segv_uids);
    528 
    529 		/*
    530 		 * Initialize a new entry with "crashes so far" of 1.
    531 		 * The expiry time is when we purge the entry if it didn't
    532 		 * reach the limit.
    533 		 */
    534 		up = kmem_alloc(sizeof(*up), KM_SLEEP);
    535 		up->sue_uid = kauth_cred_getuid(l->l_cred);
    536 		up->sue_ncrashes = 1;
    537 		up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
    538 		up->sue_suspended = 0;
    539 		LIST_INSERT_HEAD(&p->segv_uids, up, sue_list);
    540 		return 0;
    541 	}
    542 
    543 	/*
    544 	 * A program we "know" either executed or crashed again.
    545 	 * See if it's a culprit we're familiar with.
    546 	 */
    547 	uid = kauth_cred_getuid(l->l_cred);
    548 	have_uid = false;
    549 	LIST_FOREACH(up, &p->segv_uids, sue_list) {
    550 		if (up->sue_uid == uid) {
    551 			have_uid = true;
    552 			break;
    553 		}
    554 	}
    555 
    556 	/*
    557 	 * It's someone else. Add an entry for him if we crashed.
    558 	 */
    559 	if (!have_uid) {
    560 		if (crashed) {
    561 			up = kmem_alloc(sizeof(*up), KM_SLEEP);
    562 			up->sue_uid = uid;
    563 			up->sue_ncrashes = 1;
    564 			up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
    565 			up->sue_suspended = 0;
    566 			LIST_INSERT_HEAD(&p->segv_uids, up, sue_list);
    567 		}
    568 		return 0;
    569 	}
    570 
    571 	if (crashed) {
    572 		/* Check if timer on previous crashes expired first. */
    573 		if (up->sue_expiry < tv.tv_sec) {
    574 			log(LOG_INFO, "PaX Segvguard: [%s] Suspension"
    575 			    " expired.\n", name ? name : "unknown");
    576 			up->sue_ncrashes = 1;
    577 			up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
    578 			up->sue_suspended = 0;
    579 			return 0;
    580 		}
    581 
    582 		up->sue_ncrashes++;
    583 
    584 		if (up->sue_ncrashes >= pax_segvguard_maxcrashes) {
    585 			log(LOG_ALERT, "PaX Segvguard: [%s] Suspending "
    586 			    "execution for %d seconds after %zu crashes.\n",
    587 			    name ? name : "unknown", pax_segvguard_suspension,
    588 			    up->sue_ncrashes);
    589 
    590 			/* Suspend this program for a while. */
    591 			up->sue_suspended = tv.tv_sec + pax_segvguard_suspension;
    592 			up->sue_ncrashes = 0;
    593 			up->sue_expiry = 0;
    594 		}
    595 	} else {
    596 		/* Are we supposed to be suspended? */
    597 		if (up->sue_suspended > tv.tv_sec) {
    598 			log(LOG_ALERT, "PaX Segvguard: [%s] Preventing "
    599 			    "execution due to repeated segfaults.\n", name ?
    600 			    name : "unknown");
    601 			return EPERM;
    602 		}
    603 	}
    604 
    605 	return 0;
    606 }
    607 #endif /* PAX_SEGVGUARD */
    608