From 132c9174a61dbfac2cdc8081fed65382f42373c8 Mon Sep 17 00:00:00 2001 From: Shawn Webb Date: Sun, 30 Oct 2016 12:28:12 -0400 Subject: [PATCH] Introduce HardenedBSD's SEGVGUARD. --- sys/conf/NOTES | 1 + sys/conf/files | 7 +- sys/conf/options | 1 + sys/hardenedbsd/hbsd_pax_common.c | 5 + sys/hardenedbsd/hbsd_pax_log.c | 1 + sys/hardenedbsd/hbsd_pax_segvguard.c | 556 +++++++++++++++++++++++++++ sys/kern/kern_exec.c | 6 + sys/kern/kern_fork.c | 10 + sys/kern/kern_sig.c | 4 + sys/kern/vfs_syscalls.c | 4 + sys/sys/pax.h | 21 + 11 files changed, 613 insertions(+), 3 deletions(-) create mode 100644 sys/hardenedbsd/hbsd_pax_segvguard.c mode change 100755 => 100644 sys/sys/pax.h diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 7b471ac0a80..ef77f23bb6e 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -3061,6 +3061,7 @@ options GZIO # PAX and HardenedBSD related knobs options PAX # Enable the PAX framework options PAX_ASLR # Address Space Layout Randomization +options PAX_SEGVGUARD # ASLR bruteforce protection options HBSD_DEBUG # BHND(4) drivers diff --git a/sys/conf/files b/sys/conf/files index 11cb1873572..45058eb7483 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3218,9 +3218,10 @@ fs/ext2fs/ext2_subr.c optional ext2fs fs/ext2fs/ext2_vfsops.c optional ext2fs fs/ext2fs/ext2_vnops.c optional ext2fs # -hardenedbsd/hbsd_pax_common.c optional pax -hardenedbsd/hbsd_pax_log.c optional pax -hardenedbsd/hbsd_pax_aslr.c optional pax +hardenedbsd/hbsd_pax_common.c optional pax +hardenedbsd/hbsd_pax_log.c optional pax +hardenedbsd/hbsd_pax_aslr.c optional pax pax_aslr +hardenedbsd/hbsd_pax_segvguard.c optional pax pax_segvguard # isa/isa_if.m standard isa/isa_common.c optional isa diff --git a/sys/conf/options b/sys/conf/options index 36d6c46163d..0c7e460c755 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -968,6 +968,7 @@ RCTL opt_global.h # PaX-inspired hardening features PAX opt_pax.h PAX_ASLR opt_pax.h +PAX_SEGVGUARD opt_pax.h PAX_SYSCTLS opt_pax.h HBSD_DEBUG opt_pax.h diff --git a/sys/hardenedbsd/hbsd_pax_common.c b/sys/hardenedbsd/hbsd_pax_common.c index 0c3053aadc4..668c74c6c06 100644 --- a/sys/hardenedbsd/hbsd_pax_common.c +++ b/sys/hardenedbsd/hbsd_pax_common.c @@ -253,6 +253,10 @@ pax_elf(struct image_params *imgp, struct thread *td, pax_flag_t mode) #endif #endif +#ifdef PAX_SEGVGUARD + flags |= pax_segvguard_setup_flags(imgp, td, mode); +#endif + CTR3(KTR_PAX, "%s : flags = %x mode = %x", __func__, flags, mode); @@ -326,6 +330,7 @@ pax_init_prison(struct prison *pr) __func__, pr->pr_name); pax_aslr_init_prison(pr); + pax_segvguard_init_prison(pr); #ifdef COMPAT_FREEBSD32 pax_aslr_init_prison32(pr); diff --git a/sys/hardenedbsd/hbsd_pax_log.c b/sys/hardenedbsd/hbsd_pax_log.c index 39a79db6069..afc536fbd13 100644 --- a/sys/hardenedbsd/hbsd_pax_log.c +++ b/sys/hardenedbsd/hbsd_pax_log.c @@ -341,6 +341,7 @@ pax_db_printf_flags_td(struct thread *td, pax_log_settings_t flags) __HARDENING_LOG_TEMPLATE(HBSD, INTERNAL, pax, internal); __HARDENING_LOG_TEMPLATE(HBSD, ASLR, pax, aslr); +__HARDENING_LOG_TEMPLATE(HBSD, SEGVGUARD, pax, segvguard); void pax_log_internal_imgp(struct image_params *imgp, pax_log_settings_t flags, const char* fmt, ...) diff --git a/sys/hardenedbsd/hbsd_pax_segvguard.c b/sys/hardenedbsd/hbsd_pax_segvguard.c new file mode 100644 index 00000000000..efa564d5dca --- /dev/null +++ b/sys/hardenedbsd/hbsd_pax_segvguard.c @@ -0,0 +1,556 @@ +/*- + * Copyright (c) 2006 Elad Efrat + * Copyright (c) 2013-2015, by Oliver Pinter + * Copyright (c) 2014, by Shawn Webb + * Copyright (c) 2014, by Danilo Egea Gondolfo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_compat.h" +#include "opt_pax.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hbsd_pax_internal.h" + +#define PAX_SEGVGUARD_EXPIRY (2 * 60) +#define PAX_SEGVGUARD_SUSPENSION (10 * 60) +#define PAX_SEGVGUARD_MAXCRASHES 5 + +FEATURE(hbsd_segvguard, "Segmentation fault protection."); + +static int pax_segvguard_status = PAX_FEATURE_OPTOUT; +static int pax_segvguard_expiry = PAX_SEGVGUARD_EXPIRY; +static int pax_segvguard_suspension = PAX_SEGVGUARD_SUSPENSION; +static int pax_segvguard_maxcrashes = PAX_SEGVGUARD_MAXCRASHES; + + +struct pax_segvguard_entry { + uid_t se_uid; + ino_t se_inode; + char se_mntpoint[MNAMELEN]; + + size_t se_ncrashes; + sbintime_t se_expiry; + sbintime_t se_suspended; + LIST_ENTRY(pax_segvguard_entry) se_entry; +}; + +struct pax_segvguard_key { + uid_t se_uid; + ino_t se_inode; + char se_mntpoint[MNAMELEN]; +}; + +struct pax_segvguard_entryhead { + struct pax_segvguard_entry *lh_first; + struct mtx bucket_mtx; +}; + +static struct pax_segvguard_entryhead *pax_segvguard_hashtbl; +static int pax_segvguard_hashsize = 512; + +#define PAX_SEGVGUARD_HASHVAL(x) \ + fnv_32_buf((&(x)), sizeof(x), FNV1_32_INIT) + +#define PAX_SEGVGUARD_HASH(x) \ + (&pax_segvguard_hashtbl[PAX_SEGVGUARD_HASHVAL(x) % pax_segvguard_hashsize]) + +#define PAX_SEGVGUARD_KEY(x) \ + ((struct pax_segvguard_key *) x) + +#define PAX_SEGVGUARD_LOCK_INIT(bucket) \ + mtx_init(&(bucket)->bucket_mtx, "segvguard mutex", NULL, MTX_DEF) + +#define PAX_SEGVGUARD_LOCK(bucket) \ + mtx_lock(&bucket->bucket_mtx) + +#define PAX_SEGVGUARD_UNLOCK(bucket) \ + mtx_unlock(&bucket->bucket_mtx) + + +MALLOC_DECLARE(M_PAX); +MALLOC_DEFINE(M_PAX, "pax_segvguard", "PaX segvguard memory"); + +TUNABLE_INT("hardening.pax.segvguard.status", &pax_segvguard_status); +TUNABLE_INT("hardening.pax.segvguard.expiry_timeout", &pax_segvguard_expiry); +TUNABLE_INT("hardening.pax.segvguard.suspend_timeout", &pax_segvguard_suspension); +TUNABLE_INT("hardening.pax.segvguard.max_crashes", &pax_segvguard_maxcrashes); + +#ifdef PAX_SYSCTLS +static int sysctl_pax_segvguard_expiry(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_segvguard_suspension(SYSCTL_HANDLER_ARGS); +static int sysctl_pax_segvguard_maxcrashes(SYSCTL_HANDLER_ARGS); + +SYSCTL_DECL(_hardening_pax); +SYSCTL_NODE(_hardening_pax, OID_AUTO, segvguard, CTLFLAG_RD, 0, "PaX segvguard"); + +SYSCTL_HBSD_4STATE(pax_segvguard_status, pr_hbsd.segvguard.status, + _hardening_pax_segvguard, status, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE); + +SYSCTL_PROC(_hardening_pax_segvguard, OID_AUTO, expiry_timeout, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_segvguard_expiry, "I", + "Entry expiry timeout (in seconds)."); + +SYSCTL_PROC(_hardening_pax_segvguard, OID_AUTO, suspend_timeout, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_segvguard_suspension, "I", + "Entry suspension timeout (in seconds)."); + +SYSCTL_PROC(_hardening_pax_segvguard, OID_AUTO, max_crashes, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + NULL, 0, sysctl_pax_segvguard_maxcrashes, "I", + "Max number of crashes before expiry."); +#endif + +#ifdef PAX_SYSCTLS +static int +sysctl_pax_segvguard_expiry(SYSCTL_HANDLER_ARGS) +{ + int err; + int val; + struct prison *pr; + + pr = pax_get_prison_td(req->td); + + val = pr->pr_hbsd.segvguard.expiry; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || (req->newptr == NULL)) + return (err); + + if (pr == &prison0) + pax_segvguard_expiry = val; + + pr->pr_hbsd.segvguard.expiry = val; + + return (0); +} + +static int +sysctl_pax_segvguard_suspension(SYSCTL_HANDLER_ARGS) +{ + int err; + int val; + struct prison *pr; + + pr = pax_get_prison_td(req->td); + + val = pr->pr_hbsd.segvguard.suspension; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || (req->newptr == NULL)) + return (err); + + if (pr == &prison0) + pax_segvguard_suspension = val; + + pr->pr_hbsd.segvguard.suspension = val; + + return (0); +} + +static int +sysctl_pax_segvguard_maxcrashes(SYSCTL_HANDLER_ARGS) +{ + int err; + int val; + struct prison *pr; + + pr = pax_get_prison_td(req->td); + + val = pr->pr_hbsd.segvguard.maxcrashes; + err = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (err || (req->newptr == NULL)) + return (err); + + if (pr == &prison0) + pax_segvguard_maxcrashes = val; + + pr->pr_hbsd.segvguard.maxcrashes = val; + + return (0); +} +#endif + +void +pax_segvguard_init_prison(struct prison *pr) +{ + struct prison *pr_p; + + if (pr == &prison0) { + /* prison0 has no parent, use globals */ + pr->pr_hbsd.segvguard.status = + pax_segvguard_status; + pr->pr_hbsd.segvguard.expiry = + pax_segvguard_expiry; + pr->pr_hbsd.segvguard.suspension = + pax_segvguard_suspension; + pr->pr_hbsd.segvguard.maxcrashes = + pax_segvguard_maxcrashes; + } else { + KASSERT(pr->pr_parent != NULL, + ("%s: pr->pr_parent == NULL", __func__)); + pr_p = pr->pr_parent; + + pr->pr_hbsd.segvguard.status = + pr_p->pr_hbsd.segvguard.status; + pr->pr_hbsd.segvguard.expiry = + pr_p->pr_hbsd.segvguard.expiry; + pr->pr_hbsd.segvguard.suspension = + pr_p->pr_hbsd.segvguard.suspension; + pr->pr_hbsd.segvguard.maxcrashes = + pr_p->pr_hbsd.segvguard.maxcrashes; + } +} + +pax_flag_t +pax_segvguard_setup_flags(struct image_params *imgp, struct thread *td, pax_flag_t mode) +{ + struct prison *pr; + struct vattr vap; + pax_flag_t flags; + uint32_t status; + int ret; + + KASSERT(imgp->proc == td->td_proc, + ("%s: imgp->proc != td->td_proc", __func__)); + + flags = 0; + status = 0; + + pr = pax_get_prison_td(td); + status = pr->pr_hbsd.segvguard.status; + + if (status == PAX_FEATURE_DISABLED) { + flags &= ~PAX_NOTE_SEGVGUARD; + flags |= PAX_NOTE_NOSEGVGUARD; + + return (flags); + } + + if (status == PAX_FEATURE_FORCE_ENABLED) { + flags |= PAX_NOTE_SEGVGUARD; + flags &= ~PAX_NOTE_NOSEGVGUARD; + + return (flags); + } + + if (status == PAX_FEATURE_OPTIN) { + /* + * If the program has setuid, enforce the + * segvguard. + */ + ret = VOP_GETATTR(imgp->vp, &vap, td->td_ucred); + if (ret != 0 || + (vap.va_mode & (S_ISUID | S_ISGID)) != 0 || + (mode & PAX_NOTE_SEGVGUARD)) { + flags |= PAX_NOTE_SEGVGUARD; + flags &= ~PAX_NOTE_NOSEGVGUARD; + } else { + flags &= ~PAX_NOTE_SEGVGUARD; + flags |= PAX_NOTE_NOSEGVGUARD; + } + + return (flags); + } + + if (status == PAX_FEATURE_OPTOUT) { + if (mode & PAX_NOTE_NOSEGVGUARD) { + flags &= ~PAX_NOTE_SEGVGUARD; + flags |= PAX_NOTE_NOSEGVGUARD; + } else { + flags |= PAX_NOTE_SEGVGUARD; + flags &= ~PAX_NOTE_NOSEGVGUARD; + } + + return (flags); + } + + /* + * unknown status, force segvguard + */ + flags |= PAX_NOTE_SEGVGUARD; + flags &= ~PAX_NOTE_NOSEGVGUARD; + + return (flags); +} + + +static bool +pax_segvguard_active(struct proc *proc) +{ + pax_flag_t flags; + + if (proc == NULL) + return (true); + + pax_get_flags(proc, &flags); + + CTR3(KTR_PAX, "%s: pid = %d p_pax = %x", + __func__, proc->p_pid, flags); + + if ((flags & PAX_NOTE_SEGVGUARD) == PAX_NOTE_SEGVGUARD) + return (true); + + if ((flags & PAX_NOTE_NOSEGVGUARD) == PAX_NOTE_NOSEGVGUARD) + return (false); + + return (true); +} + +static struct pax_segvguard_entry * +pax_segvguard_add(struct thread *td, struct vnode *vn, sbintime_t sbt) +{ + struct pax_segvguard_entry *v; + struct pax_segvguard_key *key; + struct prison *pr; + struct stat sb; + int error; + + error = vn_stat(vn, &sb, td->td_ucred, NOCRED, curthread); + if (error != 0) { + pax_log_segvguard(td->td_proc, PAX_LOG_DEFAULT, + "%s:%d stat error. Bailing.", __func__, __LINE__); + + return (NULL); + } + + pr = pax_get_prison_td(td); + + v = malloc(sizeof(struct pax_segvguard_entry), M_PAX, M_NOWAIT); + if (v == NULL) + return (NULL); + + v->se_inode = sb.st_ino; + strncpy(v->se_mntpoint, vn->v_mount->mnt_stat.f_mntonname, MNAMELEN); + + v->se_uid = td->td_ucred->cr_ruid; + v->se_ncrashes = 1; + v->se_expiry = sbt + pr->pr_hbsd.segvguard.expiry * SBT_1S; + v->se_suspended = 0; + + key = PAX_SEGVGUARD_KEY(v); + PAX_SEGVGUARD_LOCK(PAX_SEGVGUARD_HASH(*key)); + LIST_INSERT_HEAD(PAX_SEGVGUARD_HASH(*key), v, se_entry); + PAX_SEGVGUARD_UNLOCK(PAX_SEGVGUARD_HASH(*key)); + + return (v); +} + +static struct pax_segvguard_entry * +pax_segvguard_lookup(struct thread *td, struct vnode *vn) +{ + struct pax_segvguard_entry *v; + struct pax_segvguard_key sk; + struct stat sb; + int error; + + error = vn_stat(vn, &sb, td->td_ucred, NOCRED, curthread); + if (error != 0) { + pax_log_segvguard(td->td_proc, PAX_LOG_DEFAULT, + "%s:%d stat error. Bailing.", __func__, __LINE__); + + return (NULL); + } + + sk.se_inode = sb.st_ino; + strncpy(sk.se_mntpoint, vn->v_mount->mnt_stat.f_mntonname, MNAMELEN); + sk.se_uid = td->td_ucred->cr_ruid; + + PAX_SEGVGUARD_LOCK(PAX_SEGVGUARD_HASH(sk)); + LIST_FOREACH(v, PAX_SEGVGUARD_HASH(sk), se_entry) { + if (v->se_inode == sb.st_ino && + !strncmp(sk.se_mntpoint, v->se_mntpoint, MNAMELEN) && + td->td_ucred->cr_ruid == v->se_uid) { + PAX_SEGVGUARD_UNLOCK(PAX_SEGVGUARD_HASH(sk)); + + return (v); + } + } + PAX_SEGVGUARD_UNLOCK(PAX_SEGVGUARD_HASH(sk)); + + return (NULL); +} + +void +pax_segvguard_remove(struct thread *td, struct vnode *vn) +{ + struct pax_segvguard_entry *v; + struct pax_segvguard_key *key; + + v = pax_segvguard_lookup(td, vn); + + if (v != NULL) { + key = PAX_SEGVGUARD_KEY(v); + PAX_SEGVGUARD_LOCK(PAX_SEGVGUARD_HASH(*key)); + LIST_REMOVE(v, se_entry); + PAX_SEGVGUARD_UNLOCK(PAX_SEGVGUARD_HASH(*key)); + free(v, M_PAX); + } +} + +int +pax_segvguard_segfault(struct thread *td, const char *name) +{ + struct pax_segvguard_entry *se; + struct prison *pr; + struct vnode *v; + sbintime_t sbt; + + v = td->td_proc->p_textvp; + if (v == NULL) + return (EFAULT); + + pr = pax_get_prison_td(td); + + if (pax_segvguard_active(td->td_proc) == false) + return (0); + + sbt = sbinuptime(); + + se = pax_segvguard_lookup(td, v); + + /* + * If a program we don't know about crashed, we need to create a new entry for it + */ + if (se == NULL) { + pax_segvguard_add(td, v, sbt); + } else { + if (se->se_expiry < sbt && se->se_suspended <= sbt) { + pax_log_segvguard(td->td_proc, PAX_LOG_DEFAULT, + "[%s (%d)] Suspension expired.", name, td->td_proc->p_pid); + se->se_ncrashes = 1; + se->se_expiry = sbt + pr->pr_hbsd.segvguard.expiry * SBT_1S; + se->se_suspended = 0; + + return (0); + } + + se->se_ncrashes++; + + if (se->se_ncrashes >= pax_segvguard_maxcrashes) { + pax_log_segvguard(td->td_proc, PAX_LOG_DEFAULT, + "[%s (%d)] Suspending execution for %d seconds after %zu crashes.", + name, td->td_proc->p_pid, + pax_segvguard_suspension, se->se_ncrashes); + se->se_suspended = sbt + pr->pr_hbsd.segvguard.suspension * SBT_1S; + se->se_ncrashes = 0; + se->se_expiry = 0; + } + } + + return (0); +} + +int +pax_segvguard_check(struct thread *td, struct vnode *v, const char *name) +{ + struct pax_segvguard_entry *se; + sbintime_t sbt; + + if (v == NULL) + return (EFAULT); + + if (pax_segvguard_active(td->td_proc) == false) + return (0); + + sbt = sbinuptime(); + + se = pax_segvguard_lookup(td, v); + + if (se != NULL) { + if (se->se_expiry < sbt && se->se_suspended <= sbt) { + pax_log_segvguard(td->td_proc, PAX_LOG_DEFAULT, + "[%s (%d)] Suspension expired.", + name, td->td_proc->p_pid); + pax_segvguard_remove(td, v); + + return (0); + } + + if (se->se_suspended > sbt) { + pax_log_segvguard(td->td_proc, PAX_LOG_DEFAULT, + "[%s (%d)] Preventing execution due to repeated segfaults.", + name, td->td_proc->p_pid); + + return (EPERM); + } + } + + return (0); +} + +static void +pax_segvguard_sysinit(void) +{ + int i; + + switch (pax_segvguard_status) { + case PAX_FEATURE_DISABLED: + case PAX_FEATURE_OPTIN: + case PAX_FEATURE_OPTOUT: + case PAX_FEATURE_FORCE_ENABLED: + break; + default: + printf("[HBSD SEGVGUARD] WARNING, invalid PAX settings in loader.conf!" + " (pax_segvguard_status = %d)\n", pax_segvguard_status); + pax_segvguard_status = PAX_FEATURE_FORCE_ENABLED; + break; + } + printf("[HBSD SEGVGUARD] status: %s\n", pax_status_str[pax_segvguard_status]); + printf("[HBSD SEGVGUARD] expiry: %d sec\n", pax_segvguard_expiry); + printf("[HBSD SEGVGUARD] suspension: %d sec\n", pax_segvguard_suspension); + printf("[HBSD SEGVGUARD] maxcrashes: %d\n", pax_segvguard_maxcrashes); + + pax_segvguard_hashtbl = + malloc(pax_segvguard_hashsize * sizeof(struct pax_segvguard_entryhead), + M_PAX, M_WAITOK | M_ZERO); + + for(i = 0; i < pax_segvguard_hashsize; i++) + PAX_SEGVGUARD_LOCK_INIT(&pax_segvguard_hashtbl[i]); +} + +SYSINIT(pax_segvguard_init, SI_SUB_PAX, SI_ORDER_ANY, pax_segvguard_sysinit, NULL); diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 5038d01a448..614a231b6c0 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -619,6 +619,12 @@ interpret: goto exec_fail_dealloc; } +#ifdef PAX_SEGVGUARD + error = pax_segvguard_check(td, imgp->vp, args->fname); + if (error) + goto exec_fail_dealloc; +#endif + /* * Special interpreter operation, cleanup and loop up to try to * activate the interpreter. diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 1c722dec03d..bf8d2a4177f 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -806,6 +806,16 @@ fork1(struct thread *td, struct fork_req *fr) else MPASS(fr->fr_procp == NULL); +#ifdef PAX_SEGVGUARD + if (td->td_proc->p_pid != 0) { + error = pax_segvguard_check(curthread, + curthread->td_proc->p_textvp, + td->td_proc->p_comm); + if (error) + return (error); + } +#endif + /* Check for the undefined or unimplemented flags. */ if ((flags & ~(RFFLAGS | RFTSIGFLAGS(RFTSIGMASK))) != 0) return (EINVAL); diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 2a5e6de7637..672e81ecd1c 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_gzio.h" #include "opt_ktrace.h" +#include "opt_pax.h" #include #include @@ -3048,6 +3049,9 @@ sigexit(td, sig) td->td_ucred ? td->td_ucred->cr_uid : -1, sig &~ WCOREFLAG, sig & WCOREFLAG ? " (core dumped)" : ""); +#ifdef PAX_SEGVGUARD + pax_segvguard_segfault(curthread, p->p_comm); +#endif } else PROC_UNLOCK(p); exit1(td, 0, sig); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index ddfbc8fbbac..4f5747a29fd 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_compat.h" #include "opt_ktrace.h" +#include "opt_pax.h" #include #include @@ -1758,6 +1759,9 @@ restart: &nd.ni_cnd); if (error != 0) goto out; +#endif +#ifdef PAX_SEGVGUARD + pax_segvguard_remove(td, vp); #endif vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK); error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); diff --git a/sys/sys/pax.h b/sys/sys/pax.h old mode 100755 new mode 100644 index 2c696719fa0..fd54d561f91 --- a/sys/sys/pax.h +++ b/sys/sys/pax.h @@ -39,6 +39,12 @@ struct hbsd_features { int compat_status; /* (p) PaX ASLR enabled (compat32) */ int disallow_map32bit_status; /* (p) MAP_32BIT protection (__LP64__ only) */ } aslr; + struct hbsd_segvguard { + int status; /* (p) PaX segvguard enabled */ + int expiry; /* (p) num of seconds to expire an entry */ + int suspension; /* (p) num of seconds to suspend an application */ + int maxcrashes; /* (p) Maximum number of crashes before suspending application */ + } segvguard; struct hbsd_log { int log; /* (p) Per-jail logging status */ int ulog; /* (p) Per-jail user visible logging status */ @@ -135,6 +141,8 @@ void pax_log_internal_imgp(struct image_params *imgp, pax_log_settings_t flags, void pax_ulog_internal(const char *fmt, ...) __printflike(1, 2); void pax_log_aslr(struct proc *, pax_log_settings_t flags, const char *fmt, ...) __printflike(3, 4); void pax_ulog_aslr(const char *fmt, ...) __printflike(1, 2); +void pax_log_segvguard(struct proc *, pax_log_settings_t flags, const char *fmt, ...) __printflike(3, 4); +void pax_ulog_segvguard(const char *fmt, ...) __printflike(1, 2); /* * Hardening related functions @@ -146,6 +154,19 @@ void pax_hardening_init_prison(struct prison *pr); #endif int pax_procfs_harden(struct thread *td); +/* + * SegvGuard related functions + */ +#ifdef PAX_SEGVGUARD +void pax_segvguard_init_prison(struct prison *pr); +#else +#define pax_segvguard_init_prison(pr) do {} while (0) +#endif +int pax_segvguard_check(struct thread *, struct vnode *, const char *); +int pax_segvguard_segfault(struct thread *, const char *); +void pax_segvguard_remove(struct thread *td, struct vnode *vn); +pax_flag_t pax_segvguard_setup_flags(struct image_params *imgp, struct thread *td, pax_flag_t mode); + #define PAX_NOTE_PAGEEXEC 0x00000001 #define PAX_NOTE_NOPAGEEXEC 0x00000002 #define PAX_NOTE_MPROTECT 0x00000004