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