1 /* $NetBSD: kern_pax.c,v 1.21 2008/06/03 22:14:24 ad Exp $ */
4 * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: kern_pax.c,v 1.21 2008/06/03 22:14:24 ad Exp $");
35 #include <sys/param.h>
37 #include <sys/exec_elf.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>
52 int pax_aslr_enabled
= 1;
53 int pax_aslr_global
= PAX_ASLR
;
55 #ifndef PAX_ASLR_DELTA_MMAP_LSB
56 #define PAX_ASLR_DELTA_MMAP_LSB PGSHIFT
58 #ifndef PAX_ASLR_DELTA_MMAP_LEN
59 #define PAX_ASLR_DELTA_MMAP_LEN ((sizeof(void *) * NBBY) / 2)
61 #ifndef PAX_ASLR_DELTA_STACK_LSB
62 #define PAX_ASLR_DELTA_STACK_LSB PGSHIFT
64 #ifndef PAX_ASLR_DELTA_STACK_LEN
65 #define PAX_ASLR_DELTA_STACK_LEN 12
71 static int pax_mprotect_enabled
= 1;
72 static int pax_mprotect_global
= PAX_MPROTECT
;
73 #endif /* PAX_MPROTECT */
76 #ifndef PAX_SEGVGUARD_EXPIRY
77 #define PAX_SEGVGUARD_EXPIRY (2 * 60)
80 #ifndef PAX_SEGVGUARD_SUSPENSION
81 #define PAX_SEGVGUARD_SUSPENSION (10 * 60)
84 #ifndef PAX_SEGVGUARD_MAXCRASHES
85 #define PAX_SEGVGUARD_MAXCRASHES 5
88 static int pax_segvguard_enabled
= 1;
89 static int pax_segvguard_global
= PAX_SEGVGUARD
;
90 static int pax_segvguard_expiry
= PAX_SEGVGUARD_EXPIRY
;
91 static int pax_segvguard_suspension
= PAX_SEGVGUARD_SUSPENSION
;
92 static int pax_segvguard_maxcrashes
= PAX_SEGVGUARD_MAXCRASHES
;
94 static fileassoc_t segvguard_id
;
96 struct pax_segvguard_uid_entry
{
100 time_t sue_suspended
;
101 LIST_ENTRY(pax_segvguard_uid_entry
) sue_list
;
104 struct pax_segvguard_entry
{
105 LIST_HEAD(, pax_segvguard_uid_entry
) segv_uids
;
108 static void pax_segvguard_cb(void *);
109 #endif /* PAX_SEGVGUARD */
111 /* PaX internal setspecific flags */
112 #define PAX_MPROTECT_EXPLICIT_ENABLE (void *)0x01
113 #define PAX_MPROTECT_EXPLICIT_DISABLE (void *)0x02
114 #define PAX_SEGVGUARD_EXPLICIT_ENABLE (void *)0x03
115 #define PAX_SEGVGUARD_EXPLICIT_DISABLE (void *)0x04
116 #define PAX_ASLR_EXPLICIT_ENABLE (void *)0x05
117 #define PAX_ASLR_EXPLICIT_DISABLE (void *)0x06
119 SYSCTL_SETUP(sysctl_security_pax_setup
, "sysctl security.pax setup")
121 const struct sysctlnode
*rnode
= NULL
, *cnode
;
123 sysctl_createv(clog
, 0, NULL
, &rnode
,
125 CTLTYPE_NODE
, "security", NULL
,
127 CTL_SECURITY
, CTL_EOL
);
129 sysctl_createv(clog
, 0, &rnode
, &rnode
,
132 SYSCTL_DESCR("PaX (exploit mitigation) features."),
134 CTL_CREATE
, CTL_EOL
);
140 sysctl_createv(clog
, 0, &rnode
, &rnode
,
142 CTLTYPE_NODE
, "mprotect",
143 SYSCTL_DESCR("mprotect(2) W^X restrictions."),
145 CTL_CREATE
, CTL_EOL
);
146 sysctl_createv(clog
, 0, &rnode
, NULL
,
147 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
148 CTLTYPE_INT
, "enabled",
149 SYSCTL_DESCR("Restrictions enabled."),
150 NULL
, 0, &pax_mprotect_enabled
, 0,
151 CTL_CREATE
, CTL_EOL
);
152 sysctl_createv(clog
, 0, &rnode
, NULL
,
153 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
154 CTLTYPE_INT
, "global",
155 SYSCTL_DESCR("When enabled, unless explicitly "
156 "specified, apply restrictions to "
158 NULL
, 0, &pax_mprotect_global
, 0,
159 CTL_CREATE
, CTL_EOL
);
160 #endif /* PAX_MPROTECT */
164 sysctl_createv(clog
, 0, &rnode
, &rnode
,
166 CTLTYPE_NODE
, "segvguard",
167 SYSCTL_DESCR("PaX segvguard."),
169 CTL_CREATE
, CTL_EOL
);
170 sysctl_createv(clog
, 0, &rnode
, NULL
,
171 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
172 CTLTYPE_INT
, "enabled",
173 SYSCTL_DESCR("segvguard enabled."),
174 NULL
, 0, &pax_segvguard_enabled
, 0,
175 CTL_CREATE
, CTL_EOL
);
176 sysctl_createv(clog
, 0, &rnode
, NULL
,
177 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
178 CTLTYPE_INT
, "global",
179 SYSCTL_DESCR("segvguard all programs."),
180 NULL
, 0, &pax_segvguard_global
, 0,
181 CTL_CREATE
, CTL_EOL
);
182 sysctl_createv(clog
, 0, &rnode
, NULL
,
183 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
184 CTLTYPE_INT
, "expiry_timeout",
185 SYSCTL_DESCR("Entry expiry timeout (in seconds)."),
186 NULL
, 0, &pax_segvguard_expiry
, 0,
187 CTL_CREATE
, CTL_EOL
);
188 sysctl_createv(clog
, 0, &rnode
, NULL
,
189 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
190 CTLTYPE_INT
, "suspend_timeout",
191 SYSCTL_DESCR("Entry suspension timeout (in seconds)."),
192 NULL
, 0, &pax_segvguard_suspension
, 0,
193 CTL_CREATE
, CTL_EOL
);
194 sysctl_createv(clog
, 0, &rnode
, NULL
,
195 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
196 CTLTYPE_INT
, "max_crashes",
197 SYSCTL_DESCR("Max number of crashes before expiry."),
198 NULL
, 0, &pax_segvguard_maxcrashes
, 0,
199 CTL_CREATE
, CTL_EOL
);
200 #endif /* PAX_SEGVGUARD */
204 sysctl_createv(clog
, 0, &rnode
, &rnode
,
206 CTLTYPE_NODE
, "aslr",
207 SYSCTL_DESCR("Address Space Layout Randomization."),
209 CTL_CREATE
, CTL_EOL
);
210 sysctl_createv(clog
, 0, &rnode
, NULL
,
211 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
212 CTLTYPE_INT
, "enabled",
213 SYSCTL_DESCR("Restrictions enabled."),
214 NULL
, 0, &pax_aslr_enabled
, 0,
215 CTL_CREATE
, CTL_EOL
);
216 sysctl_createv(clog
, 0, &rnode
, NULL
,
217 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
,
218 CTLTYPE_INT
, "global",
219 SYSCTL_DESCR("When enabled, unless explicitly "
220 "specified, apply to all processes."),
221 NULL
, 0, &pax_aslr_global
, 0,
222 CTL_CREATE
, CTL_EOL
);
223 sysctl_createv(clog
, 0, &rnode
, NULL
,
224 CTLFLAG_PERMANENT
|CTLFLAG_IMMEDIATE
,
225 CTLTYPE_INT
, "mmap_len",
226 SYSCTL_DESCR("Number of bits randomized for "
228 NULL
, PAX_ASLR_DELTA_MMAP_LEN
, NULL
, 0,
229 CTL_CREATE
, CTL_EOL
);
230 sysctl_createv(clog
, 0, &rnode
, NULL
,
231 CTLFLAG_PERMANENT
|CTLFLAG_IMMEDIATE
,
232 CTLTYPE_INT
, "stack_len",
233 SYSCTL_DESCR("Number of bits randomized for "
235 NULL
, PAX_ASLR_DELTA_STACK_LEN
, NULL
, 0,
236 CTL_CREATE
, CTL_EOL
);
237 sysctl_createv(clog
, 0, &rnode
, NULL
,
238 CTLFLAG_PERMANENT
|CTLFLAG_IMMEDIATE
,
239 CTLTYPE_INT
, "exec_len",
240 SYSCTL_DESCR("Number of bits randomized for "
241 "the PIE exec base."),
242 NULL
, PAX_ASLR_DELTA_EXEC_LEN
, NULL
, 0,
243 CTL_CREATE
, CTL_EOL
);
245 #endif /* PAX_ASLR */
256 #endif /* PAX_SEGVGUARD */
259 error
= fileassoc_register("segvguard", pax_segvguard_cb
,
262 panic("pax_init: segvguard_id: error=%d\n", error
);
264 #endif /* PAX_SEGVGUARD */
269 pax_mprotect(struct lwp
*l
, vm_prot_t
*prot
, vm_prot_t
*maxprot
)
273 if (!pax_mprotect_enabled
)
276 f
= l
->l_proc
->p_pax
;
277 if ((pax_mprotect_global
&& (f
& ELF_NOTE_PAX_NOMPROTECT
) != 0) ||
278 (!pax_mprotect_global
&& (f
& ELF_NOTE_PAX_MPROTECT
) == 0))
281 if ((*prot
& (VM_PROT_WRITE
|VM_PROT_EXECUTE
)) != VM_PROT_EXECUTE
) {
282 *prot
&= ~VM_PROT_EXECUTE
;
283 *maxprot
&= ~VM_PROT_EXECUTE
;
285 *prot
&= ~VM_PROT_WRITE
;
286 *maxprot
&= ~VM_PROT_WRITE
;
289 #endif /* PAX_MPROTECT */
293 pax_aslr_active(struct lwp
*l
)
297 if (!pax_aslr_enabled
)
300 f
= l
->l_proc
->p_pax
;
301 if ((pax_aslr_global
&& (f
& ELF_NOTE_PAX_NOASLR
) != 0) ||
302 (!pax_aslr_global
&& (f
& ELF_NOTE_PAX_ASLR
) == 0))
308 pax_aslr_init(struct lwp
*l
, struct vmspace
*vm
)
310 if (!pax_aslr_active(l
))
313 vm
->vm_aslr_delta_mmap
= PAX_ASLR_DELTA(arc4random(),
314 PAX_ASLR_DELTA_MMAP_LSB
, PAX_ASLR_DELTA_MMAP_LEN
);
318 pax_aslr(struct lwp
*l
, vaddr_t
*addr
, vaddr_t orig_addr
, int f
)
320 if (!pax_aslr_active(l
))
323 if (!(f
& MAP_FIXED
) && ((orig_addr
== 0) || !(f
& MAP_ANON
))) {
325 uprintf("applying to 0x%lx orig_addr=0x%lx f=%x\n",
326 (unsigned long)*addr
, (unsigned long)orig_addr
, f
);
328 if (!(l
->l_proc
->p_vmspace
->vm_map
.flags
& VM_MAP_TOPDOWN
))
329 *addr
+= l
->l_proc
->p_vmspace
->vm_aslr_delta_mmap
;
331 *addr
-= l
->l_proc
->p_vmspace
->vm_aslr_delta_mmap
;
333 uprintf("result 0x%lx\n", *addr
);
338 uprintf("not applying to 0x%lx orig_addr=0x%lx f=%x\n",
339 (unsigned long)*addr
, (unsigned long)orig_addr
, f
);
344 pax_aslr_stack(struct lwp
*l
, struct exec_package
*epp
, u_long
*max_stack_size
)
346 if (pax_aslr_active(l
)) {
347 u_long d
= PAX_ASLR_DELTA(arc4random(),
348 PAX_ASLR_DELTA_STACK_LSB
,
349 PAX_ASLR_DELTA_STACK_LEN
);
351 uprintf("stack 0x%lx d=0x%lx 0x%lx\n",
352 epp
->ep_minsaddr
, d
, epp
->ep_minsaddr
- d
);
354 epp
->ep_minsaddr
-= d
;
355 *max_stack_size
-= d
;
358 #endif /* PAX_ASLR */
362 pax_segvguard_cb(void *v
)
364 struct pax_segvguard_entry
*p
;
365 struct pax_segvguard_uid_entry
*up
;
371 while ((up
= LIST_FIRST(&p
->segv_uids
)) != NULL
) {
372 LIST_REMOVE(up
, sue_list
);
380 * Called when a process of image vp generated a segfault.
383 pax_segvguard(struct lwp
*l
, struct vnode
*vp
, const char *name
,
386 struct pax_segvguard_entry
*p
;
387 struct pax_segvguard_uid_entry
*up
;
393 if (!pax_segvguard_enabled
)
396 f
= l
->l_proc
->p_pax
;
397 if ((pax_segvguard_global
&& (f
& ELF_NOTE_PAX_NOGUARD
) != 0) ||
398 (!pax_segvguard_global
&& (f
& ELF_NOTE_PAX_GUARD
) == 0))
404 /* Check if we already monitor the file. */
405 p
= fileassoc_lookup(vp
, segvguard_id
);
407 /* Fast-path if starting a program we don't know. */
408 if (p
== NULL
&& !crashed
)
414 * If a program we don't know crashed, we need to create a new entry
418 p
= malloc(sizeof(*p
), M_TEMP
, M_WAITOK
);
419 fileassoc_add(vp
, segvguard_id
, p
);
420 LIST_INIT(&p
->segv_uids
);
423 * Initialize a new entry with "crashes so far" of 1.
424 * The expiry time is when we purge the entry if it didn't
427 up
= malloc(sizeof(*up
), M_TEMP
, M_WAITOK
);
428 up
->sue_uid
= kauth_cred_getuid(l
->l_cred
);
429 up
->sue_ncrashes
= 1;
430 up
->sue_expiry
= tv
.tv_sec
+ pax_segvguard_expiry
;
431 up
->sue_suspended
= 0;
433 LIST_INSERT_HEAD(&p
->segv_uids
, up
, sue_list
);
439 * A program we "know" either executed or crashed again.
440 * See if it's a culprit we're familiar with.
442 uid
= kauth_cred_getuid(l
->l_cred
);
444 LIST_FOREACH(up
, &p
->segv_uids
, sue_list
) {
445 if (up
->sue_uid
== uid
) {
452 * It's someone else. Add an entry for him if we crashed.
456 up
= malloc(sizeof(*up
), M_TEMP
, M_WAITOK
);
458 up
->sue_ncrashes
= 1;
459 up
->sue_expiry
= tv
.tv_sec
+ pax_segvguard_expiry
;
460 up
->sue_suspended
= 0;
462 LIST_INSERT_HEAD(&p
->segv_uids
, up
, sue_list
);
469 /* Check if timer on previous crashes expired first. */
470 if (up
->sue_expiry
< tv
.tv_sec
) {
471 log(LOG_INFO
, "PaX Segvguard: [%s] Suspension"
472 " expired.\n", name
? name
: "unknown");
474 up
->sue_ncrashes
= 1;
475 up
->sue_expiry
= tv
.tv_sec
+ pax_segvguard_expiry
;
476 up
->sue_suspended
= 0;
483 if (up
->sue_ncrashes
>= pax_segvguard_maxcrashes
) {
484 log(LOG_ALERT
, "PaX Segvguard: [%s] Suspending "
485 "execution for %d seconds after %zu crashes.\n",
486 name
? name
: "unknown", pax_segvguard_suspension
,
489 /* Suspend this program for a while. */
490 up
->sue_suspended
= tv
.tv_sec
+ pax_segvguard_suspension
;
491 up
->sue_ncrashes
= 0;
495 /* Are we supposed to be suspended? */
496 if (up
->sue_suspended
> tv
.tv_sec
) {
497 log(LOG_ALERT
, "PaX Segvguard: [%s] Preventing "
498 "execution due to repeated segfaults.\n", name
?
507 #endif /* PAX_SEGVGUARD */