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@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
90int pax_aslr_enabled = 1;
91int 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
106static bool pax_aslr_elf_flags_active(uint32_t);
107#endif /* PAX_ASLR */
108
109#ifdef PAX_MPROTECT
110static int pax_mprotect_enabled = 1;
111static int pax_mprotect_global = PAX_MPROTECT;
112static 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
127int pax_aslr_debug;
128#endif
129
130static int pax_segvguard_enabled = 1;
131static int pax_segvguard_global = PAX_SEGVGUARD;
132static int pax_segvguard_expiry = PAX_SEGVGUARD_EXPIRY;
133static int pax_segvguard_suspension = PAX_SEGVGUARD_SUSPENSION;
134static int pax_segvguard_maxcrashes = PAX_SEGVGUARD_MAXCRASHES;
135
136static fileassoc_t segvguard_id;
137
138struct 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
146struct pax_segvguard_entry {
147	LIST_HEAD(, pax_segvguard_uid_entry) segv_uids;
148};
149
150static bool pax_segvguard_elf_flags_active(uint32_t);
151static void pax_segvguard_cleanup_cb(void *);
152#endif /* PAX_SEGVGUARD */
153
154SYSCTL_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 */
288void
289pax_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
302void
303pax_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)
327static inline bool
328pax_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
337static bool
338pax_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
353void
354pax_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
373static bool
374pax_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
389bool
390pax_aslr_epp_active(struct exec_package *epp)
391{
392	return pax_flags_active(epp->ep_pax_flags, P_PAX_ASLR);
393}
394
395bool
396pax_aslr_active(struct lwp *l)
397{
398	return pax_flags_active(l->l_proc->p_pax, P_PAX_ASLR);
399}
400
401void
402pax_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
412void
413pax_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
439void
440pax_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
458static bool
459pax_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
474static void
475pax_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 */
493int
494pax_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