xref: /netbsd-src/sys/kern/kern_pax.c (revision b817d381342c63f879b8ba9ab0ac5f531badebe9)
1 /*	$NetBSD: kern_pax.c,v 1.36 2016/03/20 16:39:36 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@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.36 2016/03/20 16:39:36 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_MMAP_LEN32
100 #define PAX_ASLR_DELTA_MMAP_LEN32	((sizeof(uint32_t) * NBBY) / 2)
101 #endif
102 #ifndef PAX_ASLR_DELTA_STACK_LSB
103 #define PAX_ASLR_DELTA_STACK_LSB	PGSHIFT
104 #endif
105 #ifndef PAX_ASLR_DELTA_STACK_LEN
106 #define PAX_ASLR_DELTA_STACK_LEN 	12
107 #endif
108 
109 static bool pax_aslr_elf_flags_active(uint32_t);
110 #endif /* PAX_ASLR */
111 
112 #ifdef PAX_MPROTECT
113 static int pax_mprotect_enabled = 1;
114 static int pax_mprotect_global = PAX_MPROTECT;
115 static bool pax_mprotect_elf_flags_active(uint32_t);
116 #endif /* PAX_MPROTECT */
117 
118 #ifdef PAX_SEGVGUARD
119 #ifndef PAX_SEGVGUARD_EXPIRY
120 #define	PAX_SEGVGUARD_EXPIRY		(2 * 60)
121 #endif
122 #ifndef PAX_SEGVGUARD_SUSPENSION
123 #define	PAX_SEGVGUARD_SUSPENSION	(10 * 60)
124 #endif
125 #ifndef	PAX_SEGVGUARD_MAXCRASHES
126 #define	PAX_SEGVGUARD_MAXCRASHES	5
127 #endif
128 
129 #ifdef PAX_ASLR_DEBUG
130 int pax_aslr_debug;
131 #endif
132 
133 static int pax_segvguard_enabled = 1;
134 static int pax_segvguard_global = PAX_SEGVGUARD;
135 static int pax_segvguard_expiry = PAX_SEGVGUARD_EXPIRY;
136 static int pax_segvguard_suspension = PAX_SEGVGUARD_SUSPENSION;
137 static int pax_segvguard_maxcrashes = PAX_SEGVGUARD_MAXCRASHES;
138 
139 static fileassoc_t segvguard_id;
140 
141 struct pax_segvguard_uid_entry {
142 	uid_t sue_uid;
143 	size_t sue_ncrashes;
144 	time_t sue_expiry;
145 	time_t sue_suspended;
146 	LIST_ENTRY(pax_segvguard_uid_entry) sue_list;
147 };
148 
149 struct pax_segvguard_entry {
150 	LIST_HEAD(, pax_segvguard_uid_entry) segv_uids;
151 };
152 
153 static bool pax_segvguard_elf_flags_active(uint32_t);
154 static void pax_segvguard_cleanup_cb(void *);
155 #endif /* PAX_SEGVGUARD */
156 
157 SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup")
158 {
159 	const struct sysctlnode *rnode = NULL, *cnode;
160 
161 	sysctl_createv(clog, 0, NULL, &rnode,
162 		       CTLFLAG_PERMANENT,
163 		       CTLTYPE_NODE, "pax",
164 		       SYSCTL_DESCR("PaX (exploit mitigation) features."),
165 		       NULL, 0, NULL, 0,
166 		       CTL_SECURITY, CTL_CREATE, CTL_EOL);
167 
168 	cnode = rnode;
169 
170 #ifdef PAX_MPROTECT
171 	rnode = cnode;
172 	sysctl_createv(clog, 0, &rnode, &rnode,
173 		       CTLFLAG_PERMANENT,
174 		       CTLTYPE_NODE, "mprotect",
175 		       SYSCTL_DESCR("mprotect(2) W^X restrictions."),
176 		       NULL, 0, NULL, 0,
177 		       CTL_CREATE, CTL_EOL);
178 	sysctl_createv(clog, 0, &rnode, NULL,
179 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
180 		       CTLTYPE_INT, "enabled",
181 		       SYSCTL_DESCR("Restrictions enabled."),
182 		       NULL, 0, &pax_mprotect_enabled, 0,
183 		       CTL_CREATE, CTL_EOL);
184 	sysctl_createv(clog, 0, &rnode, NULL,
185 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
186 		       CTLTYPE_INT, "global",
187 		       SYSCTL_DESCR("When enabled, unless explicitly "
188 				    "specified, apply restrictions to "
189 				    "all processes."),
190 		       NULL, 0, &pax_mprotect_global, 0,
191 		       CTL_CREATE, CTL_EOL);
192 #endif /* PAX_MPROTECT */
193 
194 #ifdef PAX_SEGVGUARD
195 	rnode = cnode;
196 	sysctl_createv(clog, 0, &rnode, &rnode,
197 		       CTLFLAG_PERMANENT,
198 		       CTLTYPE_NODE, "segvguard",
199 		       SYSCTL_DESCR("PaX segvguard."),
200 		       NULL, 0, NULL, 0,
201 		       CTL_CREATE, CTL_EOL);
202 	sysctl_createv(clog, 0, &rnode, NULL,
203 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
204 		       CTLTYPE_INT, "enabled",
205 		       SYSCTL_DESCR("segvguard enabled."),
206 		       NULL, 0, &pax_segvguard_enabled, 0,
207 		       CTL_CREATE, CTL_EOL);
208 	sysctl_createv(clog, 0, &rnode, NULL,
209 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
210 		       CTLTYPE_INT, "global",
211 		       SYSCTL_DESCR("segvguard all programs."),
212 		       NULL, 0, &pax_segvguard_global, 0,
213 		       CTL_CREATE, CTL_EOL);
214 	sysctl_createv(clog, 0, &rnode, NULL,
215 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
216 		       CTLTYPE_INT, "expiry_timeout",
217 		       SYSCTL_DESCR("Entry expiry timeout (in seconds)."),
218 		       NULL, 0, &pax_segvguard_expiry, 0,
219 		       CTL_CREATE, CTL_EOL);
220 	sysctl_createv(clog, 0, &rnode, NULL,
221 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
222 		       CTLTYPE_INT, "suspend_timeout",
223 		       SYSCTL_DESCR("Entry suspension timeout (in seconds)."),
224 		       NULL, 0, &pax_segvguard_suspension, 0,
225 		       CTL_CREATE, CTL_EOL);
226 	sysctl_createv(clog, 0, &rnode, NULL,
227 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
228 		       CTLTYPE_INT, "max_crashes",
229 		       SYSCTL_DESCR("Max number of crashes before expiry."),
230 		       NULL, 0, &pax_segvguard_maxcrashes, 0,
231 		       CTL_CREATE, CTL_EOL);
232 #endif /* PAX_SEGVGUARD */
233 
234 #ifdef PAX_ASLR
235 	rnode = cnode;
236 	sysctl_createv(clog, 0, &rnode, &rnode,
237 		       CTLFLAG_PERMANENT,
238 		       CTLTYPE_NODE, "aslr",
239 		       SYSCTL_DESCR("Address Space Layout Randomization."),
240 		       NULL, 0, NULL, 0,
241 		       CTL_CREATE, CTL_EOL);
242 	sysctl_createv(clog, 0, &rnode, NULL,
243 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
244 		       CTLTYPE_INT, "enabled",
245 		       SYSCTL_DESCR("Restrictions enabled."),
246 		       NULL, 0, &pax_aslr_enabled, 0,
247 		       CTL_CREATE, CTL_EOL);
248 	sysctl_createv(clog, 0, &rnode, NULL,
249 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
250 		       CTLTYPE_INT, "global",
251 		       SYSCTL_DESCR("When enabled, unless explicitly "
252 				    "specified, apply to all processes."),
253 		       NULL, 0, &pax_aslr_global, 0,
254 		       CTL_CREATE, CTL_EOL);
255 #ifdef PAX_ASLR_DEBUG
256 	sysctl_createv(clog, 0, &rnode, NULL,
257 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
258 		       CTLTYPE_INT, "debug",
259 		       SYSCTL_DESCR("Pring ASLR selected addresses."),
260 		       NULL, 0, &pax_aslr_debug, 0,
261 		       CTL_CREATE, CTL_EOL);
262 #endif
263 	sysctl_createv(clog, 0, &rnode, NULL,
264 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
265 		       CTLTYPE_INT, "mmap_len",
266 		       SYSCTL_DESCR("Number of bits randomized for "
267 				    "mmap(2) calls."),
268 		       NULL, PAX_ASLR_DELTA_MMAP_LEN, NULL, 0,
269 		       CTL_CREATE, CTL_EOL);
270 	sysctl_createv(clog, 0, &rnode, NULL,
271 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
272 		       CTLTYPE_INT, "stack_len",
273 		       SYSCTL_DESCR("Number of bits randomized for "
274 				    "the stack."),
275 		       NULL, PAX_ASLR_DELTA_STACK_LEN, NULL, 0,
276 		       CTL_CREATE, CTL_EOL);
277 	sysctl_createv(clog, 0, &rnode, NULL,
278 		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
279 		       CTLTYPE_INT, "exec_len",
280 		       SYSCTL_DESCR("Number of bits randomized for "
281 				    "the PIE exec base."),
282 		       NULL, PAX_ASLR_DELTA_EXEC_LEN, NULL, 0,
283 		       CTL_CREATE, CTL_EOL);
284 
285 #endif /* PAX_ASLR */
286 }
287 
288 /*
289  * Initialize PaX.
290  */
291 void
292 pax_init(void)
293 {
294 #ifdef PAX_SEGVGUARD
295 	int error;
296 
297 	error = fileassoc_register("segvguard", pax_segvguard_cleanup_cb,
298 	    &segvguard_id);
299 	if (error) {
300 		panic("pax_init: segvguard_id: error=%d\n", error);
301 	}
302 #endif /* PAX_SEGVGUARD */
303 }
304 
305 void
306 pax_setup_elf_flags(struct exec_package *epp, uint32_t elf_flags)
307 {
308 	uint32_t flags = 0;
309 
310 #ifdef PAX_ASLR
311 	if (pax_aslr_elf_flags_active(elf_flags)) {
312 		flags |= P_PAX_ASLR;
313 	}
314 #endif
315 #ifdef PAX_MPROTECT
316 	if (pax_mprotect_elf_flags_active(elf_flags)) {
317 		flags |= P_PAX_MPROTECT;
318 	}
319 #endif
320 #ifdef PAX_SEGVGUARD
321 	if (pax_segvguard_elf_flags_active(elf_flags)) {
322 		flags |= P_PAX_GUARD;
323 	}
324 #endif
325 
326 	epp->ep_pax_flags = flags;
327 }
328 
329 #if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD) || defined(PAX_ASLR)
330 static inline bool
331 pax_flags_active(uint32_t flags, uint32_t opt)
332 {
333 	if (!(flags & opt))
334 		return false;
335 	return true;
336 }
337 #endif /* PAX_MPROTECT || PAX_SEGVGUARD || PAX_ASLR */
338 
339 #ifdef PAX_MPROTECT
340 static bool
341 pax_mprotect_elf_flags_active(uint32_t flags)
342 {
343 	if (!pax_mprotect_enabled)
344 		return false;
345 	if (pax_mprotect_global && (flags & ELF_NOTE_PAX_NOMPROTECT) != 0) {
346 		/* Mprotect explicitly disabled */
347 		return false;
348 	}
349 	if (!pax_mprotect_global && (flags & ELF_NOTE_PAX_MPROTECT) == 0) {
350 		/* Mprotect not requested */
351 		return false;
352 	}
353 	return true;
354 }
355 
356 void
357 pax_mprotect(struct lwp *l, vm_prot_t *prot, vm_prot_t *maxprot)
358 {
359 	uint32_t flags;
360 
361 	flags = l->l_proc->p_pax;
362 	if (!pax_flags_active(flags, P_PAX_MPROTECT))
363 		return;
364 
365 	if ((*prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) != VM_PROT_EXECUTE) {
366 		*prot &= ~VM_PROT_EXECUTE;
367 		*maxprot &= ~VM_PROT_EXECUTE;
368 	} else {
369 		*prot &= ~VM_PROT_WRITE;
370 		*maxprot &= ~VM_PROT_WRITE;
371 	}
372 }
373 #endif /* PAX_MPROTECT */
374 
375 #ifdef PAX_ASLR
376 static bool
377 pax_aslr_elf_flags_active(uint32_t flags)
378 {
379 	if (!pax_aslr_enabled)
380 		return false;
381 	if (pax_aslr_global && (flags & ELF_NOTE_PAX_NOASLR) != 0) {
382 		/* ASLR explicitly disabled */
383 		return false;
384 	}
385 	if (!pax_aslr_global && (flags & ELF_NOTE_PAX_ASLR) == 0) {
386 		/* ASLR not requested */
387 		return false;
388 	}
389 	return true;
390 }
391 
392 bool
393 pax_aslr_epp_active(struct exec_package *epp)
394 {
395 	return pax_flags_active(epp->ep_pax_flags, P_PAX_ASLR);
396 }
397 
398 bool
399 pax_aslr_active(struct lwp *l)
400 {
401 	return pax_flags_active(l->l_proc->p_pax, P_PAX_ASLR);
402 }
403 
404 void
405 pax_aslr_init_vm(struct lwp *l, struct vmspace *vm, struct exec_package *ep)
406 {
407 	if (!pax_aslr_active(l))
408 		return;
409 
410 	uint32_t len = (ep->ep_flags & EXEC_32) ?
411 	    PAX_ASLR_DELTA_MMAP_LEN32 : PAX_ASLR_DELTA_MMAP_LEN;
412 
413 	vm->vm_aslr_delta_mmap = PAX_ASLR_DELTA(cprng_fast32(),
414 	    PAX_ASLR_DELTA_MMAP_LSB, len);
415 
416 	PAX_DPRINTF("delta_mmap=%#jx/%u", vm->vm_aslr_delta_mmap, len);
417 }
418 
419 void
420 pax_aslr_mmap(struct lwp *l, vaddr_t *addr, vaddr_t orig_addr, int f)
421 {
422 	if (!pax_aslr_active(l))
423 		return;
424 #ifdef PAX_ASLR_DEBUG
425 	char buf[256];
426 	if (pax_aslr_debug)
427 		snprintb(buf, sizeof(buf), MAP_FMT, f);
428 	else
429 		buf[0] = '\0';
430 #endif
431 
432 	if (!(f & MAP_FIXED) && ((orig_addr == 0) || !(f & MAP_ANON))) {
433 		PAX_DPRINTF("applying to %#jx orig_addr=%#jx f=%s",
434 		    (uintmax_t)*addr, (uintmax_t)orig_addr, buf);
435 		if (!(l->l_proc->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN))
436 			*addr += l->l_proc->p_vmspace->vm_aslr_delta_mmap;
437 		else
438 			*addr -= l->l_proc->p_vmspace->vm_aslr_delta_mmap;
439 		PAX_DPRINTF("result %#jx", (uintmax_t)*addr);
440 	} else {
441 		PAX_DPRINTF("not applying to %#jx orig_addr=%#jx f=%s",
442 		    (uintmax_t)*addr, (uintmax_t)orig_addr, buf);
443 	}
444 }
445 
446 void
447 pax_aslr_stack(struct exec_package *epp, u_long *max_stack_size)
448 {
449 	if (!pax_aslr_epp_active(epp))
450 		return;
451 
452 	u_long d = PAX_ASLR_DELTA(cprng_fast32(),
453 	    PAX_ASLR_DELTA_STACK_LSB,
454 	    PAX_ASLR_DELTA_STACK_LEN);
455 	PAX_DPRINTF("stack %#jx delta=%#lx diff=%lx",
456 	    (uintmax_t)epp->ep_minsaddr, d, epp->ep_minsaddr - d);
457 	epp->ep_minsaddr -= d;
458 	*max_stack_size -= d;
459 	if (epp->ep_ssize > *max_stack_size)
460 		epp->ep_ssize = *max_stack_size;
461 }
462 #endif /* PAX_ASLR */
463 
464 #ifdef PAX_SEGVGUARD
465 static bool
466 pax_segvguard_elf_flags_active(uint32_t flags)
467 {
468 	if (!pax_segvguard_enabled)
469 		return false;
470 	if (pax_segvguard_global && (flags & ELF_NOTE_PAX_NOGUARD) != 0) {
471 		/* Segvguard explicitly disabled */
472 		return false;
473 	}
474 	if (!pax_segvguard_global && (flags & ELF_NOTE_PAX_GUARD) == 0) {
475 		/* Segvguard not requested */
476 		return false;
477 	}
478 	return true;
479 }
480 
481 static void
482 pax_segvguard_cleanup_cb(void *v)
483 {
484 	struct pax_segvguard_entry *p = v;
485 	struct pax_segvguard_uid_entry *up;
486 
487 	if (p == NULL) {
488 		return;
489 	}
490 	while ((up = LIST_FIRST(&p->segv_uids)) != NULL) {
491 		LIST_REMOVE(up, sue_list);
492 		kmem_free(up, sizeof(*up));
493 	}
494 	kmem_free(p, sizeof(*p));
495 }
496 
497 /*
498  * Called when a process of image vp generated a segfault.
499  */
500 int
501 pax_segvguard(struct lwp *l, struct vnode *vp, const char *name,
502     bool crashed)
503 {
504 	struct pax_segvguard_entry *p;
505 	struct pax_segvguard_uid_entry *up;
506 	struct timeval tv;
507 	uid_t uid;
508 	uint32_t flags;
509 	bool have_uid;
510 
511 	flags = l->l_proc->p_pax;
512 	if (!pax_flags_active(flags, P_PAX_GUARD))
513 		return 0;
514 
515 	if (vp == NULL)
516 		return EFAULT;
517 
518 	/* Check if we already monitor the file. */
519 	p = fileassoc_lookup(vp, segvguard_id);
520 
521 	/* Fast-path if starting a program we don't know. */
522 	if (p == NULL && !crashed)
523 		return 0;
524 
525 	microtime(&tv);
526 
527 	/*
528 	 * If a program we don't know crashed, we need to create a new entry
529 	 * for it.
530 	 */
531 	if (p == NULL) {
532 		p = kmem_alloc(sizeof(*p), KM_SLEEP);
533 		fileassoc_add(vp, segvguard_id, p);
534 		LIST_INIT(&p->segv_uids);
535 
536 		/*
537 		 * Initialize a new entry with "crashes so far" of 1.
538 		 * The expiry time is when we purge the entry if it didn't
539 		 * reach the limit.
540 		 */
541 		up = kmem_alloc(sizeof(*up), KM_SLEEP);
542 		up->sue_uid = kauth_cred_getuid(l->l_cred);
543 		up->sue_ncrashes = 1;
544 		up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
545 		up->sue_suspended = 0;
546 		LIST_INSERT_HEAD(&p->segv_uids, up, sue_list);
547 		return 0;
548 	}
549 
550 	/*
551 	 * A program we "know" either executed or crashed again.
552 	 * See if it's a culprit we're familiar with.
553 	 */
554 	uid = kauth_cred_getuid(l->l_cred);
555 	have_uid = false;
556 	LIST_FOREACH(up, &p->segv_uids, sue_list) {
557 		if (up->sue_uid == uid) {
558 			have_uid = true;
559 			break;
560 		}
561 	}
562 
563 	/*
564 	 * It's someone else. Add an entry for him if we crashed.
565 	 */
566 	if (!have_uid) {
567 		if (crashed) {
568 			up = kmem_alloc(sizeof(*up), KM_SLEEP);
569 			up->sue_uid = uid;
570 			up->sue_ncrashes = 1;
571 			up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
572 			up->sue_suspended = 0;
573 			LIST_INSERT_HEAD(&p->segv_uids, up, sue_list);
574 		}
575 		return 0;
576 	}
577 
578 	if (crashed) {
579 		/* Check if timer on previous crashes expired first. */
580 		if (up->sue_expiry < tv.tv_sec) {
581 			log(LOG_INFO, "PaX Segvguard: [%s] Suspension"
582 			    " expired.\n", name ? name : "unknown");
583 			up->sue_ncrashes = 1;
584 			up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
585 			up->sue_suspended = 0;
586 			return 0;
587 		}
588 
589 		up->sue_ncrashes++;
590 
591 		if (up->sue_ncrashes >= pax_segvguard_maxcrashes) {
592 			log(LOG_ALERT, "PaX Segvguard: [%s] Suspending "
593 			    "execution for %d seconds after %zu crashes.\n",
594 			    name ? name : "unknown", pax_segvguard_suspension,
595 			    up->sue_ncrashes);
596 
597 			/* Suspend this program for a while. */
598 			up->sue_suspended = tv.tv_sec + pax_segvguard_suspension;
599 			up->sue_ncrashes = 0;
600 			up->sue_expiry = 0;
601 		}
602 	} else {
603 		/* Are we supposed to be suspended? */
604 		if (up->sue_suspended > tv.tv_sec) {
605 			log(LOG_ALERT, "PaX Segvguard: [%s] Preventing "
606 			    "execution due to repeated segfaults.\n", name ?
607 			    name : "unknown");
608 			return EPERM;
609 		}
610 	}
611 
612 	return 0;
613 }
614 #endif /* PAX_SEGVGUARD */
615