diff options
| author | elad <elad@NetBSD.org> | 2006-11-22 02:02:51 +0000 |
|---|---|---|
| committer | elad <elad@NetBSD.org> | 2006-11-22 02:02:51 +0000 |
| commit | a84fee7faffe0455d7a4d4f03c03d697bde6849c (patch) | |
| tree | a598a3231359a9c3ccc229b5b9c2aa9f340660fa /sys | |
| parent | 25e0bf9073a7cb7cb3443ad5b3b97717c19b18ac (diff) | |
Initial implementation of PaX Segvguard (this is still work-in-progress,
it's just to get it out of my local tree).
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/conf/files | 5 | ||||
| -rw-r--r-- | sys/kern/exec_elf32.c | 13 | ||||
| -rw-r--r-- | sys/kern/init_main.c | 13 | ||||
| -rw-r--r-- | sys/kern/kern_exec.c | 15 | ||||
| -rw-r--r-- | sys/kern/kern_pax.c | 263 | ||||
| -rw-r--r-- | sys/kern/kern_sig.c | 12 | ||||
| -rw-r--r-- | sys/sys/pax.h | 5 |
7 files changed, 301 insertions, 25 deletions
diff --git a/sys/conf/files b/sys/conf/files index 82f1d23e0c5..fad506448ab 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.816 2006/10/27 21:20:48 christos Exp $ +# $NetBSD: files,v 1.817 2006/11/22 02:02:51 elad Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -90,6 +90,7 @@ defflag opt_syscall_stats.h SYSCALL_TIMES_HASCOUNTER: SYSCALL_TIMES file kern/subr_syscall_stats.c syscall_stats defparam opt_pax.h PAX_MPROTECT + PAX_SEGVGUARD defflag opt_fileassoc.h FILEASSOC defparam opt_fileassoc.h FILEASSOC_NHOOKS @@ -1289,7 +1290,7 @@ file kern/kern_malloc.c file kern/kern_malloc_debug.c malloc_debug file kern/kern_fileassoc.c fileassoc file kern/kern_ntptime.c -file kern/kern_pax.c pax_mprotect +file kern/kern_pax.c pax_mprotect | pax_segvguard file kern/kern_physio.c file kern/kern_proc.c file kern/kern_prot.c diff --git a/sys/kern/exec_elf32.c b/sys/kern/exec_elf32.c index b7aa4f2a9c0..00aac776a56 100644 --- a/sys/kern/exec_elf32.c +++ b/sys/kern/exec_elf32.c @@ -1,4 +1,4 @@ -/* $NetBSD: exec_elf32.c,v 1.118 2006/11/22 00:41:38 elad Exp $ */ +/* $NetBSD: exec_elf32.c,v 1.119 2006/11/22 02:02:51 elad Exp $ */ /*- * Copyright (c) 1994, 2000, 2005 The NetBSD Foundation, Inc. @@ -64,7 +64,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: exec_elf32.c,v 1.118 2006/11/22 00:41:38 elad Exp $"); +__KERNEL_RCSID(1, "$NetBSD: exec_elf32.c,v 1.119 2006/11/22 02:02:51 elad Exp $"); /* If not included by exec_elf64.c, ELFSIZE won't be defined. */ #ifndef ELFSIZE @@ -91,9 +91,9 @@ __KERNEL_RCSID(1, "$NetBSD: exec_elf32.c,v 1.118 2006/11/22 00:41:38 elad Exp $" #include <machine/cpu.h> #include <machine/reg.h> -#if defined(PAX_MPROTECT) +#if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD) #include <sys/pax.h> -#endif /* PAX_MPROTECT */ +#endif /* PAX_MPROTECT || PAX_SEGVGUARD */ extern const struct emul emul_netbsd; @@ -690,11 +690,10 @@ exec_elf_makecmds(struct lwp *l, struct exec_package *epp) case PT_INTERP: /* Already did this one. */ case PT_DYNAMIC: - break; case PT_NOTE: -#if defined(PAX_MPROTECT) +#if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD) pax_adjust(l, ph[i].p_flags); -#endif /* PAX_MPROTECT */ +#endif /* PAX_MPROTECT || PAX_SEGVGUARD */ break; case PT_PHDR: diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 626176b9bd7..18520e09382 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,4 +1,4 @@ -/* $NetBSD: init_main.c,v 1.281 2006/11/22 00:41:38 elad Exp $ */ +/* $NetBSD: init_main.c,v 1.282 2006/11/22 02:02:51 elad Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1991, 1992, 1993 @@ -71,7 +71,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.281 2006/11/22 00:41:38 elad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.282 2006/11/22 02:02:51 elad Exp $"); #include "opt_ipsec.h" #include "opt_kcont.h" @@ -156,10 +156,9 @@ __KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.281 2006/11/22 00:41:38 elad Exp $") #include <sys/fileassoc.h> #endif /* FILEASSOC */ -#if defined(PAX_MPROTECT) +#if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD) #include <sys/pax.h> -#endif /* PAX_MPROTECT */ - +#endif /* PAX_MPROTECT || PAX_SEGVGUARD */ #include <ufs/ufs/quota.h> #include <miscfs/genfs/genfs.h> @@ -390,9 +389,9 @@ main(void) veriexec_init_fp_ops(); #endif /* NVERIEXEC > 0 */ -#if defined(PAX_MPROTECT) +#if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD) pax_init(); -#endif /* PAX_MPROTECT */ +#endif /* PAX_MPROTECT || PAX_SEGVGUARD */ /* Attach pseudo-devices. */ for (pdev = pdevinit; pdev->pdev_attach != NULL; pdev++) diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index b951f00b08e..7135d535727 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_exec.c,v 1.231 2006/11/01 10:17:58 yamt Exp $ */ +/* $NetBSD: kern_exec.c,v 1.232 2006/11/22 02:02:51 elad Exp $ */ /*- * Copyright (C) 1993, 1994, 1996 Christopher G. Demetriou @@ -33,12 +33,13 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.231 2006/11/01 10:17:58 yamt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.232 2006/11/22 02:02:51 elad Exp $"); #include "opt_ktrace.h" #include "opt_syscall_debug.h" #include "opt_compat_netbsd.h" #include "veriexec.h" +#include "opt_pax.h" #include <sys/param.h> #include <sys/systm.h> @@ -73,6 +74,10 @@ __KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.231 2006/11/01 10:17:58 yamt Exp $") #include <sys/systrace.h> #endif /* SYSTRACE */ +#ifdef PAX_SEGVGUARD +#include <sys/pax.h> +#endif /* PAX_SEGVGUARD */ + #include <uvm/uvm_extern.h> #include <machine/cpu.h> @@ -282,13 +287,17 @@ check_exec(struct lwp *l, struct exec_package *epp, int flag) /* unlock vp, since we need it unlocked from here on out. */ VOP_UNLOCK(vp, 0); - #if NVERIEXEC > 0 if ((error = veriexec_verify(l, vp, epp->ep_ndp->ni_dirp, flag, NULL)) != 0) goto bad2; #endif /* NVERIEXEC > 0 */ +#ifdef PAX_SEGVGUARD + if (pax_segvguard(l, vp, epp->ep_ndp->ni_dirp, FALSE)) + return (EPERM); +#endif /* PAX_SEGVGUARD */ + /* now we have the file, get the exec header */ uvn_attach(vp, VM_PROT_READ); error = vn_rdwr(UIO_READ, vp, epp->ep_hdr, epp->ep_hdrlen, 0, diff --git a/sys/kern/kern_pax.c b/sys/kern/kern_pax.c index 5087fc5c27f..8d55004e100 100644 --- a/sys/kern/kern_pax.c +++ b/sys/kern/kern_pax.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_pax.c,v 1.7 2006/11/22 00:41:38 elad Exp $ */ +/* $NetBSD: kern_pax.c,v 1.8 2006/11/22 02:02:51 elad Exp $ */ /*- * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org> @@ -37,21 +37,64 @@ #include <sys/exec_elf.h> #include <sys/pax.h> #include <sys/sysctl.h> +#include <sys/malloc.h> +#include <sys/fileassoc.h> +#include <sys/syslog.h> +#include <sys/vnode.h> +#include <sys/queue.h> +#include <sys/kauth.h> #ifdef PAX_MPROTECT static int pax_mprotect_enabled = 1; static int pax_mprotect_global = PAX_MPROTECT; specificdata_key_t pax_mprotect_key; -#endif /* PAX_MPROTECT */ +#endif + +#ifdef PAX_SEGVGUARD +#ifndef PAX_SEGVGUARD_EXPIRY +#define PAX_SEGVGUARD_EXPIRY (2 * 60) +#endif + +#ifndef PAX_SEGVGUARD_SUSPENSION +#define PAX_SEGVGUARD_SUSPENSION (10 * 60) +#endif + +#ifndef PAX_SEGVGUARD_MAXCRASHES +#define PAX_SEGVGUARD_MAXCRASHES 5 +#endif + +static int pax_segvguard_enabled = 1; +static int pax_segvguard_global = PAX_SEGVGUARD; +static int pax_segvguard_expiry = PAX_SEGVGUARD_EXPIRY; +static int pax_segvguard_suspension = PAX_SEGVGUARD_SUSPENSION; +static int pax_segvguard_maxcrashes = PAX_SEGVGUARD_MAXCRASHES; + +static fileassoc_t segvguard_id; +specificdata_key_t pax_segvguard_key; + +struct pax_segvguard_uid_entry { + uid_t sue_uid; + size_t sue_ncrashes; + time_t sue_expiry; + time_t sue_suspended; + LIST_ENTRY(pax_segvguard_uid_entry) sue_list; +}; + +struct pax_segvguard_entry { + LIST_HEAD(, pax_segvguard_uid_entry) segv_uids; +}; +#endif /* PAX_SEGVGUARD */ /* PaX internal setspecific flags */ #define PAX_MPROTECT_EXPLICIT_ENABLE (void *)0x01 #define PAX_MPROTECT_EXPLICIT_DISABLE (void *)0x02 +#define PAX_SEGVGUARD_EXPLICIT_ENABLE (void *)0x03 +#define PAX_SEGVGUARD_EXPLICIT_DISABLE (void *)0x04 SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup") { - const struct sysctlnode *rnode = NULL; + const struct sysctlnode *rnode = NULL, *cnode; sysctl_createv(clog, 0, NULL, &rnode, CTLFLAG_PERMANENT, @@ -66,6 +109,8 @@ SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup") NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); + cnode = rnode; + #ifdef PAX_MPROTECT sysctl_createv(clog, 0, &rnode, &rnode, CTLFLAG_PERMANENT, @@ -88,6 +133,47 @@ SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup") NULL, 0, &pax_mprotect_global, 0, CTL_CREATE, CTL_EOL); #endif /* PAX_MPROTECT */ + + rnode = cnode; + +#ifdef PAX_SEGVGUARD + sysctl_createv(clog, 0, &rnode, &rnode, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "segvguard", + SYSCTL_DESCR("PaX segvguard."), + NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, &rnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "enabled", + SYSCTL_DESCR("segvguard enabled."), + NULL, 0, &pax_segvguard_enabled, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, &rnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "global", + SYSCTL_DESCR("segvguard all programs."), + NULL, 0, &pax_segvguard_global, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, &rnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "expiry_timeout", + SYSCTL_DESCR("Entry expiry timeout (in seconds)."), + NULL, 0, &pax_segvguard_expiry, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, &rnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "suspend_timeout", + SYSCTL_DESCR("Entry suspension timeout (in seconds)."), + NULL, 0, &pax_segvguard_suspension, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, &rnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "max_crashes", + SYSCTL_DESCR("Max number of crashes before expiry."), + NULL, 0, &pax_segvguard_maxcrashes, 0, + CTL_CREATE, CTL_EOL); +#endif /* PAX_SEGVGUARD */ } /* @@ -99,6 +185,11 @@ pax_init(void) #ifdef PAX_MPROTECT proc_specific_key_create(&pax_mprotect_key, NULL); #endif /* PAX_MPROTECT */ + +#ifdef PAX_SEGVGUARD + segvguard_id = fileassoc_register("segvguard", pax_segvguard_cb); + proc_specific_key_create(&pax_segvguard_key, NULL); +#endif /* PAX_SEGVGUARD */ } void @@ -114,6 +205,18 @@ pax_adjust(struct lwp *l, int f) PAX_MPROTECT_EXPLICIT_DISABLE); } #endif /* PAX_MPROTECT */ + +#ifdef PAX_SEGVGUARD + if (pax_segvguard_enabled) { + if (f & PF_PAXGUARD) { + proc_setspecific(l->l_proc, pax_segvguard_key, + PAX_SEGVGUARD_EXPLICIT_ENABLE); + } + if (f & PF_PAXNOGUARD) + proc_setspecific(l->l_proc, pax_segvguard_key, + PAX_SEGVGUARD_EXPLICIT_DISABLE); + } +#endif /* PAX_SEGVGUARD */ } #ifdef PAX_MPROTECT @@ -139,3 +242,157 @@ pax_mprotect(struct lwp *l, vm_prot_t *prot, vm_prot_t *maxprot) } } #endif /* PAX_MPROTECT */ + +#ifdef PAX_SEGVGUARD +void +pax_segvguard_cb(void *v, int what) +{ + struct pax_segvguard_entry *p; + struct pax_segvguard_uid_entry *up; + + if (v == NULL) + return; + + if (what == FILEASSOC_CLEANUP_FILE) { + p = v; + while ((up = LIST_FIRST(&p->segv_uids)) != NULL) { + LIST_REMOVE(up, sue_list); + free(up, M_TEMP); + } + } + + free(v, M_TEMP); +} + +/* + * Called when a process of image vp generated a segfault. + */ +int +pax_segvguard(struct lwp *l, struct vnode *vp, const char *name, + boolean_t crashed) +{ + struct pax_segvguard_entry *p; + struct pax_segvguard_uid_entry *up; + struct timeval tv; + uid_t uid; + void *t; + boolean_t have_uid; + + if (!pax_segvguard_enabled) + return (0); + + t = proc_getspecific(l->l_proc, pax_segvguard_key); + if ((pax_segvguard_global && t == PAX_SEGVGUARD_EXPLICIT_DISABLE) || + (!pax_segvguard_global && t != PAX_SEGVGUARD_EXPLICIT_ENABLE)) + return (0); + + if (segvguard_id == FILEASSOC_INVAL || vp == NULL) + return (EFAULT); + + /* Check if we already monitor the file. */ + p = fileassoc_lookup(vp, segvguard_id); + + /* Fast-path if starting a program we don't know. */ + if (p == NULL && !crashed) + return (0); + + microtime(&tv); + + /* + * If a program we don't know crashed, we need to create a new entry + * for it. + */ + if (p == NULL) { + p = malloc(sizeof(*p), M_TEMP, M_WAITOK); + if (fileassoc_add(vp, segvguard_id, p) != 0) { + fileassoc_table_add(vp->v_mount, 16); + fileassoc_add(vp, segvguard_id, p); + } + LIST_INIT(&p->segv_uids); + + /* + * Initialize a new entry with "crashes so far" of 1. + * The expiry time is when we purge the entry if it didn't + * reach the limit. + */ + up = malloc(sizeof(*up), M_TEMP, M_WAITOK); + up->sue_uid = kauth_cred_getuid(l->l_cred); + up->sue_ncrashes = 1; + up->sue_expiry = tv.tv_sec + pax_segvguard_expiry; + up->sue_suspended = 0; + + LIST_INSERT_HEAD(&p->segv_uids, up, sue_list); + + return (0); + } + + /* + * A program we "know" either executed or crashed again. + * See if it's a culprit we're familiar with. + */ + uid = kauth_cred_getuid(l->l_cred); + have_uid = FALSE; + LIST_FOREACH(up, &p->segv_uids, sue_list) { + if (up->sue_uid == uid) { + have_uid = TRUE; + break; + } + } + + /* + * It's someone else. Add an entry for him if we crashed. + */ + if (!have_uid) { + if (crashed) { + up = malloc(sizeof(*up), M_TEMP, M_WAITOK); + up->sue_uid = uid; + up->sue_ncrashes = 1; + up->sue_expiry = tv.tv_sec + pax_segvguard_expiry; + up->sue_suspended = 0; + + LIST_INSERT_HEAD(&p->segv_uids, up, sue_list); + } + + return (0); + } + + if (crashed) { + /* Check if timer on previous crashes expired first. */ + if (up->sue_expiry < tv.tv_sec) { + log(LOG_INFO, "PaX Segvguard: [%s] Suspension" + " expired.\n", name ? name : "unknown"); + + up->sue_ncrashes = 1; + up->sue_expiry = tv.tv_sec + pax_segvguard_expiry; + up->sue_suspended = 0; + + return (0); + } + + up->sue_ncrashes++; + + if (up->sue_ncrashes >= pax_segvguard_maxcrashes) { + log(LOG_ALERT, "PaX Segvguard: [%s] Suspending " + "execution for %d seconds after %zu crashes.\n", + name ? name : "unknown", pax_segvguard_suspension, + up->sue_ncrashes); + + /* Suspend this program for a while. */ + up->sue_suspended = tv.tv_sec + pax_segvguard_suspension; + up->sue_ncrashes = 0; + up->sue_expiry = 0; + } + } else { + /* Are we supposed to be suspended? */ + if (up->sue_suspended > tv.tv_sec) { + log(LOG_ALERT, "PaX Segvguard: [%s] Preventing " + "execution due to repeated segfaults.\n", name ? + name : "unknown"); + + return (EPERM); + } + } + + return (0); +} +#endif /* PAX_SEGVGUARD */ diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index dd5e0e75dd2..7648e8dd03e 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_sig.c,v 1.239 2006/11/08 20:18:33 drochner Exp $ */ +/* $NetBSD: kern_sig.c,v 1.240 2006/11/22 02:02:51 elad Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.239 2006/11/08 20:18:33 drochner Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.240 2006/11/22 02:02:51 elad Exp $"); #include "opt_coredump.h" #include "opt_ktrace.h" @@ -46,6 +46,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.239 2006/11/08 20:18:33 drochner Exp #include "opt_compat_sunos.h" #include "opt_compat_netbsd.h" #include "opt_compat_netbsd32.h" +#include "opt_pax.h" #define SIGPROP /* include signal properties table */ #include <sys/param.h> @@ -83,6 +84,10 @@ __KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.239 2006/11/08 20:18:33 drochner Exp #include <sys/user.h> /* for coredump */ +#ifdef PAX_SEGVGUARD +#include <sys/pax.h> +#endif /* PAX_SEGVGUARD */ + #include <uvm/uvm.h> #include <uvm/uvm_extern.h> @@ -2085,6 +2090,9 @@ sigexit(struct lwp *l, int signum) p->p_comm, uid, signum); } +#ifdef PAX_SEGVGUARD + pax_segvguard(l, p->p_textvp, p->p_comm, TRUE); +#endif /* PAX_SEGVGUARD */ } exit1(l, W_EXITCODE(0, exitsig)); diff --git a/sys/sys/pax.h b/sys/sys/pax.h index c8bd5952b70..1b27c1f89d1 100644 --- a/sys/sys/pax.h +++ b/sys/sys/pax.h @@ -1,4 +1,4 @@ -/* $NetBSD: pax.h,v 1.4 2006/11/22 00:41:38 elad Exp $ */ +/* $NetBSD: pax.h,v 1.5 2006/11/22 02:02:51 elad Exp $ */ /*- * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org> @@ -42,5 +42,8 @@ void pax_adjust(struct lwp *, int); void pax_mprotect(struct lwp *, vm_prot_t *, vm_prot_t *); +int pax_segvguard(struct lwp *, struct vnode *, const char *, boolean_t); +void pax_segvguard_cb(void *, int); + #endif /* !__SYS_PAX_H__ */ |
