From e13c0d42ebbd46d953ef28ac43162e1db14f9ed4 Mon Sep 17 00:00:00 2001 From: Shawn Webb Date: Mon, 21 Mar 2016 21:02:14 -0400 Subject: [PATCH] HBSD OPNsense: Separate out the ASLR code. On OPNsense's 16.7 roadmap is HardenedBSD's ASLR code. This commit separates out the ASLR code from the rest of our exploit mitigation and system hardening code. Testing and verification still need to be performed. Initial testing (compile + boot + `procstat -v PIDofPIEapplication) has been performed. More thorough testing should occur. Shared object load order randomization in the RTLD is not included in this patch. That will be discussed with the fine folks at OPNsense at a later time. Since OPNsense is based on FreeBSD 10.x, this patch will need to be backported to 10-STABLE. However, a "horizontal port" to 11-CURRENT, which is what this commit is, needed to be done first. Signed-off-by: Shawn Webb --- sys/amd64/amd64/elf_machdep.c | 4 + sys/amd64/amd64/machdep.c | 2 +- sys/amd64/conf/GENERIC | 3 + sys/amd64/ia32/ia32_signal.c | 6 +- sys/amd64/ia32/ia32_syscall.c | 2 +- sys/amd64/include/vmparam.h | 7 +- sys/amd64/linux32/linux32_machdep.c | 2 +- sys/amd64/linux32/linux32_sysvec.c | 11 +- sys/arm/arm/elf_machdep.c | 4 + sys/arm/arm/machdep.c | 4 +- sys/compat/freebsd32/freebsd32_misc.c | 14 +- sys/compat/ia32/ia32_sysvec.c | 3 + sys/conf/NOTES | 5 + sys/conf/files | 4 + sys/conf/options | 15 + sys/conf/options.amd64 | 6 + sys/conf/options.sparc64 | 6 + sys/ddb/db_ps.c | 9 + sys/hardenedbsd/hbsd_pax_aslr.c | 858 ++++++++++++++++++++++++++ sys/hardenedbsd/hbsd_pax_common.c | 334 ++++++++++ sys/hardenedbsd/hbsd_pax_internal.h | 107 ++++ sys/hardenedbsd/hbsd_pax_log.c | 380 ++++++++++++ sys/i386/conf/GENERIC | 3 + sys/i386/i386/elf_machdep.c | 3 + sys/i386/i386/machdep.c | 10 +- sys/i386/linux/linux_machdep.c | 2 +- sys/i386/linux/linux_sysvec.c | 8 +- sys/kern/imgact_elf.c | 37 +- sys/kern/init_main.c | 15 +- sys/kern/kern_exec.c | 89 ++- sys/kern/kern_fork.c | 1 + sys/kern/kern_jail.c | 30 + sys/kern/kern_mib.c | 2 + sys/kern/kern_proc.c | 24 +- sys/kern/kern_resource.c | 6 +- sys/kern/kern_thr.c | 1 + sys/kern/subr_prf.c | 45 ++ sys/kern/sys_process.c | 2 + sys/mips/mips/elf_machdep.c | 5 + sys/mips/mips/freebsd32_machdep.c | 5 +- sys/mips/mips/pm_machdep.c | 2 +- sys/powerpc/powerpc/elf32_machdep.c | 4 + sys/powerpc/powerpc/elf64_machdep.c | 4 + sys/powerpc/powerpc/exec_machdep.c | 2 +- sys/sparc64/sparc64/elf_machdep.c | 4 + sys/sparc64/sparc64/machdep.c | 2 +- sys/sys/jail.h | 5 + sys/sys/kernel.h | 1 + sys/sys/pax.h | 179 ++++++ sys/sys/proc.h | 7 + sys/sys/sysctl.h | 1 + sys/sys/sysent.h | 2 + sys/vm/vm_map.c | 24 +- sys/vm/vm_map.h | 7 + sys/vm/vm_mmap.c | 23 +- 55 files changed, 2259 insertions(+), 82 deletions(-) create mode 100644 sys/hardenedbsd/hbsd_pax_aslr.c create mode 100644 sys/hardenedbsd/hbsd_pax_common.c create mode 100644 sys/hardenedbsd/hbsd_pax_internal.h create mode 100644 sys/hardenedbsd/hbsd_pax_log.c create mode 100644 sys/sys/pax.h diff --git a/sys/amd64/amd64/elf_machdep.c b/sys/amd64/amd64/elf_machdep.c index 23fa39b4fd1..7656b53de25 100644 --- a/sys/amd64/amd64/elf_machdep.c +++ b/sys/amd64/amd64/elf_machdep.c @@ -26,12 +26,15 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include #include #include #include +#include #include #include #include @@ -82,6 +85,7 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace, }; INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 51f59887963..542b73bde85 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -425,7 +425,7 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_rsp = (long)sfp; - regs->tf_rip = p->p_sysent->sv_sigcode_base; + regs->tf_rip = p->p_sigcode_base; regs->tf_rflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index afc2122234a..f2e98d350f1 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -364,3 +364,6 @@ device xenpci # Xen HVM Hypervisor services driver # VMware support device vmx # VMware VMXNET3 Ethernet + +options PAX +options PAX_ASLR diff --git a/sys/amd64/ia32/ia32_signal.c b/sys/amd64/ia32/ia32_signal.c index da01647d61d..0280e146621 100644 --- a/sys/amd64/ia32/ia32_signal.c +++ b/sys/amd64/ia32/ia32_signal.c @@ -419,7 +419,7 @@ ia32_osendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_rsp = (uintptr_t)fp; - regs->tf_rip = p->p_sysent->sv_psstrings - sz_ia32_osigcode; + regs->tf_rip = p->p_ps_strings - sz_ia32_osigcode; regs->tf_rflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucode32sel; regs->tf_ds = _udatasel; @@ -534,7 +534,7 @@ freebsd4_ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_rsp = (uintptr_t)sfp; - regs->tf_rip = p->p_sysent->sv_sigcode_base + sz_ia32_sigcode - + regs->tf_rip = p->p_sigcode_base + sz_ia32_sigcode - sz_freebsd4_ia32_sigcode; regs->tf_rflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucode32sel; @@ -682,7 +682,7 @@ ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_rsp = (uintptr_t)sfp; - regs->tf_rip = p->p_sysent->sv_sigcode_base; + regs->tf_rip = p->p_sigcode_base; regs->tf_rflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucode32sel; regs->tf_ss = _udatasel; diff --git a/sys/amd64/ia32/ia32_syscall.c b/sys/amd64/ia32/ia32_syscall.c index 6e96eddb00a..99dfc590d42 100644 --- a/sys/amd64/ia32/ia32_syscall.c +++ b/sys/amd64/ia32/ia32_syscall.c @@ -230,7 +230,7 @@ setup_lcall_gate(void) bzero(&uap, sizeof(uap)); uap.start = 0; uap.num = 1; - lcall_addr = curproc->p_sysent->sv_psstrings - sz_lcall_tramp; + lcall_addr = curproc->p_psstrings - sz_lcall_tramp; bzero(&desc, sizeof(desc)); desc.sd_type = SDT_MEMERA; desc.sd_dpl = SEL_UPL; diff --git a/sys/amd64/include/vmparam.h b/sys/amd64/include/vmparam.h index 91066c62f63..e051bb81123 100644 --- a/sys/amd64/include/vmparam.h +++ b/sys/amd64/include/vmparam.h @@ -63,7 +63,7 @@ #define DFLSSIZ (8UL*1024*1024) /* initial stack size limit */ #endif #ifndef MAXSSIZ -#define MAXSSIZ (512UL*1024*1024) /* max stack size */ +#define MAXSSIZ (1UL*1024*1024*1024) /* max stack size */ #endif #ifndef SGROWSIZ #define SGROWSIZ (128UL*1024) /* amount to grow stack */ @@ -178,10 +178,11 @@ #define VM_MAXUSER_ADDRESS UVADDR(NUPML4E, 0, 0, 0) #define SHAREDPAGE (VM_MAXUSER_ADDRESS - PAGE_SIZE) -#define USRSTACK SHAREDPAGE +#define SHAREDPAGE_GUARD (4 * PAGE_SIZE) +#define USRSTACK (SHAREDPAGE - SHAREDPAGE_GUARD) #define VM_MAX_ADDRESS UPT_MAX_ADDRESS -#define VM_MIN_ADDRESS (0) +#define VM_MIN_ADDRESS (65536) #define PHYS_TO_DMAP(x) ((x) | DMAP_MIN_ADDRESS) #define DMAP_TO_PHYS(x) ((x) & ~DMAP_MIN_ADDRESS) diff --git a/sys/amd64/linux32/linux32_machdep.c b/sys/amd64/linux32/linux32_machdep.c index 155e90b1f4b..84f3a229f92 100644 --- a/sys/amd64/linux32/linux32_machdep.c +++ b/sys/amd64/linux32/linux32_machdep.c @@ -635,7 +635,7 @@ linux_mmap_common(struct thread *td, l_uintptr_t addr, l_size_t len, l_int prot, * mmap's return value. */ PROC_LOCK(p); - p->p_vmspace->vm_maxsaddr = (char *)LINUX32_USRSTACK - + p->p_vmspace->vm_maxsaddr = (char *)p->p_usrstack - lim_cur(p, RLIMIT_STACK); PROC_UNLOCK(p); } diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c index 42af635fd35..9307555a554 100644 --- a/sys/amd64/linux32/linux32_sysvec.c +++ b/sys/amd64/linux32/linux32_sysvec.c @@ -33,6 +33,7 @@ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" +#include "opt_pax.h" #ifndef COMPAT_FREEBSD32 #error "Unable to compile Linux-emulator due to missing COMPAT_FREEBSD32 option!" @@ -51,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -250,11 +252,11 @@ elf_linux_fixup(register_t **stack_base, struct image_params *imgp) struct linux32_ps_strings *arginfo; int issetugid; - arginfo = (struct linux32_ps_strings *)LINUX32_PS_STRINGS; - uplatform = (Elf32_Addr *)((caddr_t)arginfo - linux_szplatform); - KASSERT(curthread->td_proc == imgp->proc, ("unsafe elf_linux_fixup(), should be curproc")); + + arginfo = (struct linux32_ps_strings *)imgp->proc->p_psstrings; + uplatform = (Elf32_Addr *)((caddr_t)arginfo - linux_szplatform); base = (Elf32_Addr *)*stack_base; args = (Elf32_Auxargs *)imgp->auxargs; pos = base + (imgp->args->argc + imgp->args->envc + 2); @@ -869,7 +871,7 @@ linux_copyout_strings(struct image_params *imgp) * Calculate string base and vector table pointers. * Also deal with signal trampoline code for this exec type. */ - arginfo = (struct linux32_ps_strings *)LINUX32_PS_STRINGS; + arginfo = (struct linux32_ps_strings *)imgp->proc->p_psstrings; destp = (caddr_t)arginfo - SPARE_USRSPACE - linux_szplatform - roundup((ARG_MAX - imgp->args->stringspace), sizeof(char *)); @@ -1039,6 +1041,7 @@ struct sysentvec elf_linux_sysvec = { .sv_shared_page_base = LINUX32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = linux_schedtail, + .sv_pax_aslr_init = pax_aslr_init_vmspace32, }; INIT_SYSENTVEC(elf_sysvec, &elf_linux_sysvec); diff --git a/sys/arm/arm/elf_machdep.c b/sys/arm/arm/elf_machdep.c index 8ef9bd444c9..306118eff08 100644 --- a/sys/arm/arm/elf_machdep.c +++ b/sys/arm/arm/elf_machdep.c @@ -26,6 +26,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -34,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -79,6 +82,7 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace, }; static Elf32_Brandinfo freebsd_brand_info = { diff --git a/sys/arm/arm/machdep.c b/sys/arm/arm/machdep.c index 17e96b502fb..a01530c1de1 100644 --- a/sys/arm/arm/machdep.c +++ b/sys/arm/arm/machdep.c @@ -44,6 +44,7 @@ #include "opt_compat.h" #include "opt_ddb.h" +#include "opt_pax.h" #include "opt_platform.h" #include "opt_sched.h" #include "opt_timer.h" @@ -69,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -278,7 +280,7 @@ sendsig(catcher, ksi, mask) tf->tf_r5 = (register_t)&fp->sf_uc; tf->tf_pc = (register_t)catcher; tf->tf_usr_sp = (register_t)fp; - tf->tf_usr_lr = (register_t)(PS_STRINGS - *(p->p_sysent->sv_szsigcode)); + tf->tf_usr_lr = (register_t)(p->p_psstrings - *(p->p_sysent->sv_szsigcode)); CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_usr_lr, tf->tf_usr_sp); diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index afcef0d7880..2e4089104ae 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_pax.h" #define __ELF_WORD_SIZE 32 @@ -55,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -2865,12 +2867,16 @@ freebsd32_copyout_strings(struct image_params *imgp) execpath_len = strlen(imgp->execpath) + 1; else execpath_len = 0; - arginfo = (struct freebsd32_ps_strings *)curproc->p_sysent-> - sv_psstrings; - if (imgp->proc->p_sysent->sv_sigcode_base == 0) + arginfo = (struct freebsd32_ps_strings *)curproc->p_psstrings; + imgp->proc->p_sigcode_base = imgp->proc->p_sysent->sv_sigcode_base; + if (imgp->proc->p_sigcode_base == 0) szsigcode = *(imgp->proc->p_sysent->sv_szsigcode); - else + else { szsigcode = 0; +#ifdef PAX_ASLR + pax_aslr_vdso(imgp->proc, &(imgp->proc->p_sigcode_base)); +#endif + } destp = (uintptr_t)arginfo; /* diff --git a/sys/compat/ia32/ia32_sysvec.c b/sys/compat/ia32/ia32_sysvec.c index bfc17d681e3..8145bb8cab3 100644 --- a/sys/compat/ia32/ia32_sysvec.c +++ b/sys/compat/ia32/ia32_sysvec.c @@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" +#include "opt_pax.h" #define __ELF_WORD_SIZE 32 @@ -42,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -139,6 +141,7 @@ struct sysentvec ia32_freebsd_sysvec = { .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace32, }; INIT_SYSENTVEC(elf_ia32_sysvec, &ia32_freebsd_sysvec); diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 2c3c6ebc0a9..34ff13bf27e 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2993,3 +2993,8 @@ options RANDOM_RWFILE # Read and write entropy cache # Intel em(4) driver options EM_MULTIQUEUE # Activate multiqueue features/disable MSI-X + +# PAX and HardenedBSD related knobs +options PAX +options PAX_ASLR +options PAX_SYSCTLS diff --git a/sys/conf/files b/sys/conf/files index a11f9a954ea..2602a802dcf 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2957,6 +2957,10 @@ gnu/fs/reiserfs/reiserfs_stree.c optional reiserfs gnu/fs/reiserfs/reiserfs_vfsops.c optional reiserfs gnu/fs/reiserfs/reiserfs_vnops.c optional reiserfs # +hardenedbsd/hbsd_pax_common.c optional pax +hardenedbsd/hbsd_pax_log.c optional pax +hardenedbsd/hbsd_pax_aslr.c optional pax pax_aslr +# isa/isa_if.m standard isa/isa_common.c optional isa isa/isahint.c optional isa diff --git a/sys/conf/options b/sys/conf/options index 2e44c9a1564..cb4d7863ba7 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -930,6 +930,21 @@ RACCT_DEFAULT_TO_DISABLED opt_global.h # Resource Limits RCTL opt_global.h +# PaX-inspired hardening features +PAX opt_pax.h +PAX_ASLR opt_pax.h +PAX_SYSCTLS opt_pax.h + +# ASLR overwritable defaults +PAX_ASLR_DELTA_MMAP_DEF_LEN opt_pax.h +PAX_ASLR_DELTA_STACK_DEF_LEN opt_pax.h +PAX_ASLR_DELTA_VDSO_DEF_LEN opt_pax.h +PAX_ASLR_DELTA_EXEC_DEF_LEN opt_pax.h +PAX_ASLR_COMPAT_DELTA_MMAP_DEF_LEN opt_pax.h +PAX_ASLR_COMPAT_DELTA_STACK_DEF_LEN opt_pax.h +PAX_ASLR_COMPAT_DELTA_EXEC_DEF_LEN opt_pax.h +PAX_ASLR_COMPAT_DELTA_VDSO_DEF_LEN opt_pax.h + # Random number generator(s) RANDOM_YARROW opt_random.h RANDOM_FORTUNA opt_random.h diff --git a/sys/conf/options.amd64 b/sys/conf/options.amd64 index b2497719bd8..3954ba81c4d 100644 --- a/sys/conf/options.amd64 +++ b/sys/conf/options.amd64 @@ -71,3 +71,9 @@ HYPERV opt_global.h # options for the Intel C600 SAS driver (isci) ISCI_LOGGING opt_isci.h + +# HardenedBSD ASLR options +PAX_ASLR_DELTA_MAP32BIT_DEF_LEN opt_pax.h +PAX_ASLR_DELTA_MAP32BIT_MIN_LEN opt_pax.h +PAX_ASLR_DELTA_MAP32BIT_MAX_LEN opt_pax.h +PAX_ASLR_DELTA_MAP32BIT_LSB opt_pax.h diff --git a/sys/conf/options.sparc64 b/sys/conf/options.sparc64 index 883db166927..8b964439f3c 100644 --- a/sys/conf/options.sparc64 +++ b/sys/conf/options.sparc64 @@ -29,3 +29,9 @@ SCHIZO_DEBUG opt_schizo.h SUNKBD_DFLT_KEYMAP opt_sunkbd.h SUNKBD_EMULATE_ATKBD opt_sunkbd.h + +# HardenedBSD ASLR options +PAX_ASLR_DELTA_MAP32BIT_DEF_LEN opt_pax.h +PAX_ASLR_DELTA_MAP32BIT_MIN_LEN opt_pax.h +PAX_ASLR_DELTA_MAP32BIT_MAX_LEN opt_pax.h +PAX_ASLR_DELTA_MAP32BIT_LSB opt_pax.h diff --git a/sys/ddb/db_ps.c b/sys/ddb/db_ps.c index d28a0609931..9c3a7a295c5 100644 --- a/sys/ddb/db_ps.c +++ b/sys/ddb/db_ps.c @@ -30,10 +30,13 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include #include +#include #include #include #include @@ -316,6 +319,9 @@ DB_SHOW_COMMAND(thread, db_show_thread) (void *)(td->td_kstack + td->td_kstack_pages * PAGE_SIZE - 1)); db_printf(" flags: %#x ", td->td_flags); db_printf(" pflags: %#x\n", td->td_pflags); +#ifdef PAX + pax_db_printf_flags_td(td, PAX_LOG_DEFAULT); +#endif db_printf(" state: "); switch (td->td_state) { case TDS_INACTIVE: @@ -423,6 +429,9 @@ DB_SHOW_COMMAND(proc, db_show_proc) if (p->p_args != NULL) db_printf(" arguments: %.*s\n", (int)p->p_args->ar_length, p->p_args->ar_args); +#ifdef PAX + pax_db_printf_flags(p, PAX_LOG_DEFAULT); +#endif db_printf(" threads: %d\n", p->p_numthreads); FOREACH_THREAD_IN_PROC(p, td) { dumpthread(p, td, 1); diff --git a/sys/hardenedbsd/hbsd_pax_aslr.c b/sys/hardenedbsd/hbsd_pax_aslr.c new file mode 100644 index 00000000000..974b3d85288 --- /dev/null +++ b/sys/hardenedbsd/hbsd_pax_aslr.c @@ -0,0 +1,858 @@ +/*- + * Copyright (c) 2006 Elad Efrat + * Copyright (c) 2013-2016, by Oliver Pinter + * Copyright (c) 2014-2015, by Shawn Webb + * 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 +#include + +#include "hbsd_pax_internal.h" + +#ifndef PAX_ASLR_DELTA +#define PAX_ASLR_DELTA(delta, lsb, len) \ + (((delta) & ((1UL << (len)) - 1)) << (lsb)) +#endif /* PAX_ASLR_DELTA */ + +/*- + * generic ASLR values + * + * | 32 bit | 64 bit | compat | + * +-------+--------+--------+--------+ + * | MMAP | 14 bit | 30 bit | 14 bit | + * +-------+--------+--------+--------+ + * | STACK | 10 bit | 42 bit | 10 bit | + * +-------+--------+--------+--------+ + * | EXEC | 14 bit | 30 bit | 14 bit | + * +-------+--------+--------+--------+ + * | VDSO | 10 bit | 28 bit | 10 bit | + * +-------+--------+--------+--------+ + * | M32B | N.A. | 18 bit | N.A. | + * +-------+--------+--------+--------+ + * + */ + +#ifndef PAX_ASLR_DELTA_MMAP_LSB +#define PAX_ASLR_DELTA_MMAP_LSB PAGE_SHIFT +#endif /* PAX_ASLR_DELTA_MMAP_LSB */ + +#ifndef PAX_ASLR_DELTA_STACK_LSB +#define PAX_ASLR_DELTA_STACK_LSB PAGE_SHIFT +#endif /* PAX_ASLR_DELTA_STACK_LSB */ + +#ifndef PAX_ASLR_DELTA_STACK_WITH_GAP_LSB +#define PAX_ASLR_DELTA_STACK_WITH_GAP_LSB 3 +#endif /* PAX_ASLR_DELTA_STACK_WITH_GAP_LSB */ + +#ifndef PAX_ASLR_DELTA_EXEC_LSB +#define PAX_ASLR_DELTA_EXEC_LSB PAGE_SHIFT +#endif /* PAX_ASLR_DELTA_EXEC_LSB */ + +#ifndef PAX_ASLR_DELTA_VDSO_LSB +#define PAX_ASLR_DELTA_VDSO_LSB PAGE_SHIFT +#endif /* PAX_ASLR_DELTA_VDSO_LSB */ + +#ifdef MAP_32BIT +#ifndef PAX_ASLR_DELTA_MAP32BIT_LSB +#define PAX_ASLR_DELTA_MAP32BIT_LSB PAGE_SHIFT +#endif /* PAX_ASLR_DELTA_MAP32BIT_LSB */ +#endif /* MAP_32BIT */ + +/* + * ASLR default values for native host + */ +#ifdef __LP64__ + +#ifndef PAX_ASLR_DELTA_MMAP_DEF_LEN +#define PAX_ASLR_DELTA_MMAP_DEF_LEN 30 +#endif /* PAX_ASLR_DELTA_MMAP_DEF_LEN */ + +#ifndef PAX_ASLR_DELTA_STACK_DEF_LEN +#define PAX_ASLR_DELTA_STACK_DEF_LEN 42 +#endif /* PAX_ASLR_DELTA_STACK_DEF_LEN */ + +#ifndef PAX_ASLR_DELTA_EXEC_DEF_LEN +#define PAX_ASLR_DELTA_EXEC_DEF_LEN 30 +#endif /* PAX_ASLR_DELTA_EXEC_DEF_LEN */ + +#ifndef PAX_ASLR_DELTA_VDSO_DEF_LEN +#define PAX_ASLR_DELTA_VDSO_DEF_LEN 28 +#endif /* PAX_ASLR_DELTA_VDSO_DEF_LEN */ + +#ifdef MAP_32BIT +#ifndef PAX_ASLR_DELTA_MAP32BIT_DEF_LEN +#define PAX_ASLR_DELTA_MAP32BIT_DEF_LEN 18 +#endif /* PAX_ASLR_DELTA_MAP32BIT_DEF_LEN */ +#endif /* MAP_32BIT */ + +#else /* ! __LP64__ */ + +#ifndef PAX_ASLR_DELTA_MMAP_DEF_LEN +#define PAX_ASLR_DELTA_MMAP_DEF_LEN 14 +#endif /* PAX_ASLR_DELTA_MMAP_DEF_LEN */ + +#ifndef PAX_ASLR_DELTA_STACK_DEF_LEN +#define PAX_ASLR_DELTA_STACK_DEF_LEN 10 +#endif /* PAX_ASLR_DELTA_STACK_DEF_LEN */ + +#ifndef PAX_ASLR_DELTA_EXEC_DEF_LEN +#define PAX_ASLR_DELTA_EXEC_DEF_LEN 14 +#endif /* PAX_ASLR_DELTA_EXEC_DEF_LEN */ + +#ifndef PAX_ASLR_DELTA_VDSO_DEF_LEN +#define PAX_ASLR_DELTA_VDSO_DEF_LEN 10 +#endif /* PAX_ASLR_DELTA_VDSO_DEF_LEN */ + +#endif /* __LP64__ */ + +/* + * ASLR values for COMPAT_FREEBSD32, COMPAT_LINUX and MAP_32BIT + */ +#if defined(COMPAT_LINUX) || defined(COMPAT_FREEBSD32) || defined(MAP_32BIT) +#ifndef PAX_ASLR_COMPAT_DELTA_MMAP_LSB +#define PAX_ASLR_COMPAT_DELTA_MMAP_LSB PAGE_SHIFT +#endif /* PAX_ASLR_COMPAT_DELTA_MMAP_LSB */ + +#ifndef PAX_ASLR_COMPAT_DELTA_STACK_LSB +#define PAX_ASLR_COMPAT_DELTA_STACK_LSB 3 +#endif /* PAX_ASLR_COMPAT_DELTA_STACK_LSB */ + +#ifndef PAX_ASLR_COMPAT_DELTA_EXEC_LSB +#define PAX_ASLR_COMPAT_DELTA_EXEC_LSB PAGE_SHIFT +#endif /* PAX_ASLR_COMPAT_DELTA_EXEC_LSB */ + +#ifndef PAX_ASLR_COMPAT_DELTA_VDSO_LSB +#define PAX_ASLR_COMPAT_DELTA_VDSO_LSB PAGE_SHIFT +#endif /* PAX_ASLR_COMPAT_DELTA_VDSO_LSB */ + +#ifndef PAX_ASLR_COMPAT_DELTA_MMAP_DEF_LEN +#define PAX_ASLR_COMPAT_DELTA_MMAP_DEF_LEN 14 +#endif /* PAX_ASLR_COMPAT_DELTA_MMAP_DEF_LEN */ + +#ifndef PAX_ASLR_COMPAT_DELTA_STACK_DEF_LEN +#define PAX_ASLR_COMPAT_DELTA_STACK_DEF_LEN 10 +#endif /* PAX_ASLR_COMPAT_DELTA_STACK_DEF_LEN */ + +#ifndef PAX_ASLR_COMPAT_DELTA_EXEC_DEF_LEN +#define PAX_ASLR_COMPAT_DELTA_EXEC_DEF_LEN 14 +#endif /* PAX_ASLR_COMPAT_DELTA_EXEC_DEF_LEN */ + +#ifndef PAX_ASLR_COMPAT_DELTA_VDSO_DEF_LEN +#define PAX_ASLR_COMPAT_DELTA_VDSO_DEF_LEN 10 +#endif /* PAX_ASLR_COMPAT_DELTA_VDSO_DEF_LEN */ + +#endif + +FEATURE(hbsd_aslr, "Address Space Layout Randomization."); + +static int pax_aslr_status = PAX_FEATURE_OPTOUT; +static int pax_aslr_mmap_len = PAX_ASLR_DELTA_MMAP_DEF_LEN; +static int pax_aslr_stack_len = PAX_ASLR_DELTA_STACK_DEF_LEN; +static int pax_aslr_exec_len = PAX_ASLR_DELTA_EXEC_DEF_LEN; +static int pax_aslr_vdso_len = PAX_ASLR_DELTA_VDSO_DEF_LEN; +#ifdef MAP_32BIT +static int pax_aslr_map32bit_len = PAX_ASLR_DELTA_MAP32BIT_DEF_LEN; +#ifdef PAX_HARDENING +static int pax_disallow_map32bit_status_global = PAX_FEATURE_OPTOUT; +#else +static int pax_disallow_map32bit_status_global = PAX_FEATURE_OPTIN; +#endif +#endif + +#ifdef COMPAT_FREEBSD32 +static int pax_aslr_compat_status = PAX_FEATURE_OPTOUT; +static int pax_aslr_compat_mmap_len = PAX_ASLR_COMPAT_DELTA_MMAP_DEF_LEN; +static int pax_aslr_compat_stack_len = PAX_ASLR_COMPAT_DELTA_STACK_DEF_LEN; +static int pax_aslr_compat_exec_len = PAX_ASLR_COMPAT_DELTA_EXEC_DEF_LEN; +static int pax_aslr_compat_vdso_len = PAX_ASLR_COMPAT_DELTA_VDSO_DEF_LEN; +#endif /* COMPAT_FREEBSD32 */ + +TUNABLE_INT("hardening.pax.aslr.status", &pax_aslr_status); +TUNABLE_INT("hardening.pax.aslr.mmap_len", &pax_aslr_mmap_len); +TUNABLE_INT("hardening.pax.aslr.stack_len", &pax_aslr_stack_len); +TUNABLE_INT("hardening.pax.aslr.exec_len", &pax_aslr_exec_len); +TUNABLE_INT("hardening.pax.aslr.vdso_len", &pax_aslr_vdso_len); +#ifdef MAP_32BIT +TUNABLE_INT("hardening.pax.aslr.map32bit_len", &pax_aslr_map32bit_len); +TUNABLE_INT("hardening.pax.disallow_map32bit.status", &pax_disallow_map32bit_status_global); +#endif +#ifdef COMPAT_FREEBSD32 +TUNABLE_INT("hardening.pax.aslr.compat.status", &pax_aslr_compat_status); +TUNABLE_INT("hardening.pax.aslr.compat.mmap_len", &pax_aslr_compat_mmap_len); +TUNABLE_INT("hardening.pax.aslr.compat.stack_len", &pax_aslr_compat_stack_len); +TUNABLE_INT("hardening.pax.aslr.compat.exec_len", &pax_aslr_compat_exec_len); +TUNABLE_INT("hardening.pax.aslr.compat.vdso_len", &pax_aslr_compat_vdso_len); +#endif + +#ifdef PAX_SYSCTLS +SYSCTL_DECL(_hardening_pax); + +SYSCTL_NODE(_hardening_pax, OID_AUTO, aslr, CTLFLAG_RD, 0, + "Address Space Layout Randomization."); +SYSCTL_HBSD_4STATE(pax_aslr_status, pr_hbsd.aslr.status, + _hardening_pax_aslr, status, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE); + +/* COMPAT_FREEBSD32 and linuxulator. */ +#ifdef COMPAT_FREEBSD32 +SYSCTL_NODE(_hardening_pax_aslr, OID_AUTO, compat, CTLFLAG_RD, 0, + "Settings for COMPAT_FREEBSD32 and linuxulator."); +SYSCTL_HBSD_4STATE(pax_aslr_compat_status, pr_hbsd.aslr.compat_status, + _hardening_pax_aslr_compat, status, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON); +#endif /* COMPAT_FREEBSD32 */ + +#ifdef MAP_32BIT +SYSCTL_NODE(_hardening_pax, OID_AUTO, disallow_map32bit, CTLFLAG_RD, 0, + "Disallow MAP_32BIT mode mmap(2) calls."); +SYSCTL_HBSD_4STATE(pax_disallow_map32bit_status_global, pr_hbsd.aslr.disallow_map32bit_status, + _hardening_pax_disallow_map32bit, status, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE); +#endif /* MAP_32BIT */ + +#endif /* PAX_SYSCTLS */ + + +/* + * ASLR functions + */ + +static void +pax_aslr_sysinit(void) +{ + + switch (pax_aslr_status) { + case PAX_FEATURE_DISABLED: + case PAX_FEATURE_OPTIN: + case PAX_FEATURE_OPTOUT: + case PAX_FEATURE_FORCE_ENABLED: + break; + default: + printf("[HBSD ASLR] WARNING, invalid PAX settings in loader.conf!" + " (pax_aslr_status = %d)\n", pax_aslr_status); + pax_aslr_status = PAX_FEATURE_FORCE_ENABLED; + break; + } + printf("[HBSD ASLR] status: %s\n", pax_status_str[pax_aslr_status]); + printf("[HBSD ASLR] mmap: %d bit\n", pax_aslr_mmap_len); + printf("[HBSD ASLR] exec base: %d bit\n", pax_aslr_exec_len); + printf("[HBSD ASLR] stack: %d bit\n", pax_aslr_stack_len); + printf("[HBSD ASLR] vdso: %d bit\n", pax_aslr_vdso_len); +#ifdef MAP_32BIT + printf("[HBSD ASLR] map32bit: %d bit\n", pax_aslr_map32bit_len); + + switch (pax_disallow_map32bit_status_global) { + case PAX_FEATURE_DISABLED: + case PAX_FEATURE_OPTIN: + case PAX_FEATURE_OPTOUT: + case PAX_FEATURE_FORCE_ENABLED: + break; + default: + printf("[HBSD ASLR] WARNING, invalid settings in loader.conf!" + " (hardening.pax.disallow_map32bit.status = %d)\n", + pax_disallow_map32bit_status_global); + pax_disallow_map32bit_status_global = PAX_FEATURE_FORCE_ENABLED; + } + printf("[HBSD ASLR] disallow MAP_32BIT mode mmap: %s\n", + pax_status_str[pax_disallow_map32bit_status_global]); +#endif +} +SYSINIT(pax_aslr, SI_SUB_PAX, SI_ORDER_SECOND, pax_aslr_sysinit, NULL); + +bool +pax_aslr_active(struct proc *p) +{ + pax_flag_t flags; + + pax_get_flags(p, &flags); + + CTR3(KTR_PAX, "%s: pid = %d p_pax = %x", + __func__, p->p_pid, flags); + + if ((flags & PAX_NOTE_ASLR) == PAX_NOTE_ASLR) + return (true); + + if ((flags & PAX_NOTE_NOASLR) == PAX_NOTE_NOASLR) + return (false); + + return (true); +} + +void +pax_aslr_init_vmspace(struct proc *p) +{ + struct vmspace *vm; + unsigned long rand_buf; + int try; + + vm = p->p_vmspace; + KASSERT(vm != NULL, ("%s: vm is null", __func__)); + + arc4rand(&rand_buf, sizeof(rand_buf), 0); + vm->vm_aslr_delta_mmap = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_DELTA_MMAP_LSB, + pax_aslr_mmap_len); + + arc4rand(&rand_buf, sizeof(rand_buf), 0); + vm->vm_aslr_delta_exec = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_DELTA_EXEC_LSB, + pax_aslr_exec_len); + + try = 3; +try_again: + /* + * In stack case we generate a bigger random, which consists + * of two parts. + * The first upper part [pax_aslr_stack_len .. PAGE_SHIFT+1] + * applied to mapping, the second lower part [PAGE_SHIFT .. 3] + * applied in the mapping as gap. + */ + arc4rand(&rand_buf, sizeof(rand_buf), 0); + vm->vm_aslr_delta_stack = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_DELTA_STACK_WITH_GAP_LSB, + pax_aslr_stack_len); + vm->vm_aslr_delta_stack = ALIGN(vm->vm_aslr_delta_stack); + + arc4rand(&rand_buf, sizeof(rand_buf), 0); + rand_buf = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_DELTA_VDSO_LSB, + pax_aslr_vdso_len); + + /* + * Place the vdso between the stacktop and + * vm_max_user-PAGE_SIZE. + * + * In future this will change, to place them between the + * stack and heap. + */ + + /* + * This check required to handle the case + * when PAGE_ALIGN(vm->vm_aslr_delta_stack) == 0. + */ + if ((vm->vm_aslr_delta_stack & (-1UL << PAX_ASLR_DELTA_VDSO_LSB)) != 0) { + if (rand_buf > vm->vm_aslr_delta_stack) { + rand_buf = rand_buf % + ((unsigned long)vm->vm_aslr_delta_stack & + (-1UL << PAX_ASLR_DELTA_STACK_LSB)); + rand_buf &= (-1UL << PAX_ASLR_DELTA_VDSO_LSB); + } + } else if (try > 0) { + try--; + goto try_again; + } else { + /* XXX: Instead of 0, should we place them at the end of heap? */ + pax_log_aslr(p, PAX_LOG_DEFAULT, "%s check your /boot/loader.conf ...", __func__); + rand_buf = 0; + } + vm->vm_aslr_delta_vdso = rand_buf; + +#ifdef MAP_32BIT + arc4rand(&rand_buf, sizeof(rand_buf), 0); + vm->vm_aslr_delta_map32bit = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_DELTA_MAP32BIT_LSB, + pax_aslr_map32bit_len); +#endif + + CTR2(KTR_PAX, "%s: vm_aslr_delta_mmap=%p\n", + __func__, (void *)vm->vm_aslr_delta_mmap); + CTR2(KTR_PAX, "%s: vm_aslr_delta_stack=%p\n", + __func__, (void *)vm->vm_aslr_delta_stack); + CTR2(KTR_PAX, "%s: vm_aslr_delta_exec=%p\n", + __func__, (void *)vm->vm_aslr_delta_exec); + CTR2(KTR_PAX, "%s: vm_aslr_delta_vdso=%p\n", + __func__, (void *)vm->vm_aslr_delta_vdso); +#ifdef MAP_32BIT + CTR2(KTR_PAX, "%s: vm_aslr_delta_map32bit=%p\n", + __func__, (void *)vm->vm_aslr_delta_map32bit); +#endif +} + +#ifdef COMPAT_FREEBSD32 +static void +pax_compat_aslr_sysinit(void) +{ + + switch (pax_aslr_compat_status) { + case PAX_FEATURE_DISABLED: + case PAX_FEATURE_OPTIN: + case PAX_FEATURE_OPTOUT: + case PAX_FEATURE_FORCE_ENABLED: + break; + default: + printf("[HBSD ASLR (compat)] WARNING, invalid PAX settings in loader.conf! " + "(pax_aslr_compat_status = %d)\n", pax_aslr_compat_status); + pax_aslr_compat_status = PAX_FEATURE_FORCE_ENABLED; + break; + } + printf("[HBSD ASLR (compat)] status: %s\n", pax_status_str[pax_aslr_compat_status]); + printf("[HBSD ASLR (compat)] mmap: %d bit\n", pax_aslr_compat_mmap_len); + printf("[HBSD ASLR (compat)] exec base: %d bit\n", pax_aslr_compat_exec_len); + printf("[HBSD ASLR (compat)] stack: %d bit\n", pax_aslr_compat_stack_len); + printf("[HBSD ASLR (compat)] vdso: %d bit\n", pax_aslr_compat_vdso_len); +} +SYSINIT(pax_compat_aslr, SI_SUB_PAX, SI_ORDER_SECOND, pax_compat_aslr_sysinit, NULL); + +void +pax_aslr_init_vmspace32(struct proc *p) +{ + struct vmspace *vm; + long rand_buf; + + vm = p->p_vmspace; + KASSERT(vm != NULL, ("%s: vm is null", __func__)); + + arc4rand(&rand_buf, sizeof(rand_buf), 0); + vm->vm_aslr_delta_mmap = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_COMPAT_DELTA_MMAP_LSB, + pax_aslr_compat_mmap_len); + + arc4rand(&rand_buf, sizeof(rand_buf), 0); + vm->vm_aslr_delta_stack = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_COMPAT_DELTA_STACK_LSB, + pax_aslr_compat_stack_len); + vm->vm_aslr_delta_stack = ALIGN(vm->vm_aslr_delta_stack); + + arc4rand(&rand_buf, sizeof(rand_buf), 0); + vm->vm_aslr_delta_exec = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_COMPAT_DELTA_EXEC_LSB, + pax_aslr_compat_exec_len); + + arc4rand(&rand_buf, sizeof(rand_buf), 0); + vm->vm_aslr_delta_vdso = PAX_ASLR_DELTA(rand_buf, + PAX_ASLR_COMPAT_DELTA_VDSO_LSB, + pax_aslr_compat_vdso_len); + + CTR2(KTR_PAX, "%s: vm_aslr_delta_mmap=%p\n", + __func__, (void *)vm->vm_aslr_delta_mmap); + CTR2(KTR_PAX, "%s: vm_aslr_delta_stack=%p\n", + __func__, (void *)vm->vm_aslr_delta_stack); + CTR2(KTR_PAX, "%s: vm_aslr_delta_exec=%p\n", + __func__, (void *)vm->vm_aslr_delta_exec); + CTR2(KTR_PAX, "%s: vm_aslr_delta_vdso=%p\n", + __func__, (void *)vm->vm_aslr_delta_vdso); +} +#endif /* COMPAT_FREEBSD32 */ + +void +pax_aslr_init(struct image_params *imgp) +{ + struct proc *p; + + KASSERT(imgp != NULL, ("%s: imgp is null", __func__)); + p = imgp->proc; + + if (!pax_aslr_active(p)) + return; + + if (imgp->sysent->sv_pax_aslr_init != NULL) + imgp->sysent->sv_pax_aslr_init(p); +} + +void +pax_aslr_init_prison(struct prison *pr) +{ + struct prison *pr_p; + + CTR2(KTR_PAX, "%s: Setting prison %s PaX variables\n", + __func__, pr->pr_name); + + if (pr == &prison0) { + /* prison0 has no parent, use globals */ + pr->pr_hbsd.aslr.status = pax_aslr_status; +#ifdef MAP_32BIT + pr->pr_hbsd.aslr.disallow_map32bit_status = + pax_disallow_map32bit_status_global; +#endif + } else { + KASSERT(pr->pr_parent != NULL, + ("%s: pr->pr_parent == NULL", __func__)); + pr_p = pr->pr_parent; + + pr->pr_hbsd.aslr.status = pr_p->pr_hbsd.aslr.status; +#ifdef MAP_32BIT + pr->pr_hbsd.aslr.disallow_map32bit_status = + pr_p->pr_hbsd.aslr.disallow_map32bit_status; +#endif + } +} + +#ifdef COMPAT_FREEBSD32 +void +pax_aslr_init_prison32(struct prison *pr) +{ + struct prison *pr_p; + + CTR2(KTR_PAX, "%s: Setting prison %s PaX variables\n", + __func__, pr->pr_name); + + if (pr == &prison0) { + /* prison0 has no parent, use globals */ + + pr->pr_hbsd.aslr.compat_status = pax_aslr_compat_status; + } else { + KASSERT(pr->pr_parent != NULL, + ("%s: pr->pr_parent == NULL", __func__)); + pr_p = pr->pr_parent; + + pr->pr_hbsd.aslr.compat_status = pr_p->pr_hbsd.aslr.compat_status; + } +} +#endif /* COMPAT_FREEBSD32 */ + +void +pax_aslr_mmap(struct proc *p, vm_offset_t *addr, vm_offset_t orig_addr, int mmap_flags) +{ + + PROC_LOCK_ASSERT(p, MA_OWNED); + +#ifdef MAP_32BIT + if (((mmap_flags & MAP_32BIT) == MAP_32BIT) || !pax_aslr_active(p)) +#else + if (!pax_aslr_active(p)) +#endif + return; + +#ifdef MAP_32BIT + KASSERT((mmap_flags & MAP_32BIT) != MAP_32BIT, + ("%s: we can't handle MAP_32BIT mapping here", __func__)); +#endif + KASSERT((mmap_flags & MAP_FIXED) != MAP_FIXED, + ("%s: we can't randomize MAP_FIXED mapping", __func__)); + + /* + * From original PaX doc: + * + * PaX applies randomization (delta_mmap) to TASK_UNMAPPED_BASE in bits 12-27 + * (16 bits) and ignores the hint for file mappings (unfortunately there is + * a 'feature' in linuxthreads where the thread stack mappings do not specify + * MAP_FIXED but still expect that behaviour so the hint cannot be overriden + * for anonymous mappings). + * + * https://github.com/HardenedBSD/pax-docs-mirror/blob/master/randmmap.txt#L30 + */ + if ((orig_addr == 0) || !(mmap_flags & MAP_ANON)) { + CTR4(KTR_PAX, "%s: applying to %p orig_addr=%p mmap_flags=%x\n", + __func__, (void *)*addr, (void *)orig_addr, mmap_flags); + *addr += p->p_vmspace->vm_aslr_delta_mmap; + CTR2(KTR_PAX, "%s: result %p\n", __func__, (void *)*addr); + } else + CTR4(KTR_PAX, "%s: not applying to %p orig_addr=%p mmap_flags=%x\n", + __func__, (void *)*addr, (void *)orig_addr, mmap_flags); +} + +void +pax_aslr_rtld(struct proc *p, u_long *addr) +{ + + PROC_LOCK_ASSERT(p, MA_OWNED); + + if (!pax_aslr_active(p)) + return; + + *addr += p->p_vmspace->vm_aslr_delta_mmap; + CTR2(KTR_PAX, "%s: result %p\n", __func__, (void *)*addr); +} + +void +pax_aslr_stack(struct proc *p, vm_offset_t *addr) +{ + uintptr_t orig_addr; + uintptr_t random; + + if (!pax_aslr_active(p)) + return; + + orig_addr = *addr; + + /* + * Apply the random offset to the mapping. + * This should page aligned. + */ + random = p->p_vmspace->vm_aslr_delta_stack; + random &= (-1UL << PAX_ASLR_DELTA_STACK_LSB); + *addr -= random; + + CTR3(KTR_PAX, "%s: orig_addr=%p, new_addr=%p\n", + __func__, (void *)orig_addr, (void *)*addr); +} + +void +pax_aslr_stack_with_gap(struct proc *p, vm_offset_t *addr) +{ + uintptr_t orig_addr; + uintptr_t random; + + if (!pax_aslr_active(p)) + return; + + orig_addr = *addr; + /* + * Apply the random gap offset withing the page. + */ + random = p->p_vmspace->vm_aslr_delta_stack; + *addr -= random; + + CTR3(KTR_PAX, "%s: orig_addr=%p, new_addr=%p\n", + __func__, (void *)orig_addr, (void *)*addr); +} + +void +pax_aslr_execbase(struct proc *p, u_long *et_dyn_addrp) +{ + + if (!pax_aslr_active(p)) + return; + + *et_dyn_addrp += p->p_vmspace->vm_aslr_delta_exec; +} + +void +pax_aslr_vdso(struct proc *p, vm_offset_t *addr) +{ + uintptr_t orig_addr; + + if (!pax_aslr_active(p)) + return; + + orig_addr = *addr; + *addr -= p->p_vmspace->vm_aslr_delta_vdso; + + CTR3(KTR_PAX, "%s: orig_addr=%p, new_addr=%p\n", + __func__, (void *)orig_addr, (void *)*addr); +} + +pax_flag_t +pax_aslr_setup_flags(struct image_params *imgp, struct thread *td, pax_flag_t mode) +{ + struct prison *pr; + pax_flag_t flags; + uint32_t status; + + 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.aslr.status; + + if (status == PAX_FEATURE_DISABLED) { + flags &= ~PAX_NOTE_ASLR; + flags |= PAX_NOTE_NOASLR; + + return (flags); + } + + if (status == PAX_FEATURE_FORCE_ENABLED) { + flags |= PAX_NOTE_ASLR; + flags &= ~PAX_NOTE_NOASLR; + + return (flags); + } + + if (status == PAX_FEATURE_OPTIN) { + if (mode & PAX_NOTE_ASLR) { + flags |= PAX_NOTE_ASLR; + flags &= ~PAX_NOTE_NOASLR; + } else { + flags &= ~PAX_NOTE_ASLR; + flags |= PAX_NOTE_NOASLR; + } + + return (flags); + } + + if (status == PAX_FEATURE_OPTOUT) { + if (mode & PAX_NOTE_NOASLR) { + flags &= ~PAX_NOTE_ASLR; + flags |= PAX_NOTE_NOASLR; + } else { + flags |= PAX_NOTE_ASLR; + flags &= ~PAX_NOTE_NOASLR; + } + + return (flags); + } + + /* + * Unknown status, force ASLR. + */ + flags |= PAX_NOTE_ASLR; + flags &= ~PAX_NOTE_NOASLR; + + return (flags); +} + +#ifdef MAP_32BIT +void +pax_aslr_mmap_map_32bit(struct proc *p, vm_offset_t *addr, vm_offset_t orig_addr, int mmap_flags) +{ + + PROC_LOCK_ASSERT(p, MA_OWNED); + + if (((mmap_flags & MAP_32BIT) != MAP_32BIT) || !pax_aslr_active(p)) + return; + + KASSERT((mmap_flags & MAP_32BIT) == MAP_32BIT, + ("%s: we can't handle not MAP_32BIT mapping here", __func__)); + KASSERT((mmap_flags & MAP_FIXED) != MAP_FIXED, + ("%s: we can't randomize MAP_FIXED mapping", __func__)); + + /* + * From original PaX doc: + * + * PaX applies randomization (delta_mmap) to TASK_UNMAPPED_BASE in bits 12-27 + * (16 bits) and ignores the hint for file mappings (unfortunately there is + * a 'feature' in linuxthreads where the thread stack mappings do not specify + * MAP_FIXED but still expect that behaviour so the hint cannot be overriden + * for anonymous mappings). + * + * https://github.com/HardenedBSD/pax-docs-mirror/blob/master/randmmap.txt#L30 + */ + if ((orig_addr == 0) || !(mmap_flags & MAP_ANON)) { + CTR4(KTR_PAX, "%s: applying to %p orig_addr=%p mmap_flags=%x\n", + __func__, (void *)*addr, (void *)orig_addr, mmap_flags); + + *addr += p->p_vmspace->vm_aslr_delta_map32bit; + CTR2(KTR_PAX, "%s: result %p\n", __func__, (void *)*addr); + } +} + +bool +pax_disallow_map32bit_active(struct thread *td, int mmap_flags) +{ + pax_flag_t flags; + + if ((mmap_flags & MAP_32BIT) != MAP_32BIT) + /* + * Fast path, the mmap request does not + * contains MAP_32BIT flag. + */ + return (false); + + pax_get_flags_td(td, &flags); + + CTR3(KTR_PAX, "%S: pid = %d p_pax = %x", + __func__, td->td_proc->p_pid, flags); + + if ((flags & PAX_NOTE_DISALLOWMAP32BIT) == PAX_NOTE_DISALLOWMAP32BIT) + return (true); + + if ((flags & PAX_NOTE_NODISALLOWMAP32BIT) == PAX_NOTE_NODISALLOWMAP32BIT) + return (false); + + return (true); +} + +pax_flag_t +pax_disallow_map32bit_setup_flags(struct image_params *imgp, struct thread *td, pax_flag_t mode) +{ + struct prison *pr; + pax_flag_t flags; + uint32_t status; + + 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.aslr.disallow_map32bit_status; + + if (status == PAX_FEATURE_DISABLED) { + flags &= ~PAX_NOTE_DISALLOWMAP32BIT; + flags |= PAX_NOTE_NODISALLOWMAP32BIT; + + return (flags); + } + + if (status == PAX_FEATURE_FORCE_ENABLED) { + flags &= ~PAX_NOTE_NODISALLOWMAP32BIT; + flags |= PAX_NOTE_DISALLOWMAP32BIT; + + return (flags); + } + + if (status == PAX_FEATURE_OPTIN) { + if (mode & PAX_NOTE_DISALLOWMAP32BIT) { + flags |= PAX_NOTE_DISALLOWMAP32BIT; + flags &= ~PAX_NOTE_NODISALLOWMAP32BIT; + } else { + flags &= ~PAX_NOTE_DISALLOWMAP32BIT; + flags |= PAX_NOTE_NODISALLOWMAP32BIT; + } + + return (flags); + } + + if (status == PAX_FEATURE_OPTOUT) { + if (mode & PAX_NOTE_NODISALLOWMAP32BIT) { + flags |= PAX_NOTE_NODISALLOWMAP32BIT; + flags &= ~PAX_NOTE_DISALLOWMAP32BIT; + } else { + flags &= ~PAX_NOTE_NODISALLOWMAP32BIT; + flags |= PAX_NOTE_DISALLOWMAP32BIT; + } + + return (flags); + } + + /* Unknown status, force MAP32 restriction. */ + flags |= PAX_NOTE_DISALLOWMAP32BIT; + flags &= ~PAX_NOTE_NODISALLOWMAP32BIT; + + return (flags); +} +#endif /* MAP_32BIT */ + diff --git a/sys/hardenedbsd/hbsd_pax_common.c b/sys/hardenedbsd/hbsd_pax_common.c new file mode 100644 index 00000000000..0c3053aadc4 --- /dev/null +++ b/sys/hardenedbsd/hbsd_pax_common.c @@ -0,0 +1,334 @@ +/*- + * Copyright (c) 2006 Elad Efrat + * Copyright (c) 2013-2016, by Oliver Pinter + * Copyright (c) 2014-2015, by Shawn Webb + * 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 + +static void pax_set_flags(struct proc *p, struct thread *td, const pax_flag_t flags); +static void pax_set_flags_td(struct thread *td, const pax_flag_t flags); +static int pax_validate_flags(const pax_flag_t flags); +static int pax_check_conflicting_modes(const pax_flag_t mode); + +CTASSERT((sizeof((struct proc *)NULL)->p_pax) == sizeof(pax_flag_t)); +CTASSERT((sizeof((struct thread *)NULL)->td_pax) == sizeof(pax_flag_t)); + +SYSCTL_NODE(_hardening, OID_AUTO, pax, CTLFLAG_RD, 0, + "PaX (exploit mitigation) features."); + +const char *pax_status_str[] = { + [PAX_FEATURE_DISABLED] = "disabled", + [PAX_FEATURE_OPTIN] = "opt-in", + [PAX_FEATURE_OPTOUT] = "opt-out", + [PAX_FEATURE_FORCE_ENABLED] = "force enabled", +}; + +const char *pax_status_simple_str[] = { + [PAX_FEATURE_SIMPLE_DISABLED] = "disabled", + [PAX_FEATURE_SIMPLE_ENABLED] = "enabled" +}; + + +/* + * @brief Get the current process prison. + * + * @param p The current process pointer. + * + * @return prion0's address if failed or kernel process + * the actual process' prison's address else + * + */ +struct prison * +pax_get_prison(struct proc *p) +{ + + KASSERT(p != NULL, ("%s: p == NULL", __func__)); + + PROC_LOCK_ASSERT(p, MA_OWNED); + + if (p->p_ucred == NULL) + return (&prison0); + + return (p->p_ucred->cr_prison); +} + +struct prison * +pax_get_prison_td(struct thread *td) +{ + + if (td == NULL || td->td_ucred == NULL) + return (&prison0); + + return (td->td_ucred->cr_prison); +} + +/* + * @brief Get the current PaX status from process. + * + * @param p The controlled process pointer. + * @param flags Where to write the current state. + * + * @return none + */ +void +pax_get_flags(struct proc *p, pax_flag_t *flags) +{ + + KASSERT(p == curthread->td_proc, + ("%s: p != curthread->td_proc", __func__)); + +#ifdef HBSD_DEBUG + struct thread *td; + + FOREACH_THREAD_IN_PROC(p, td) { + KASSERT(td->td_pax == p->p_pax, ("%s: td->td_pax != p->p_pax", + __func__)); + } +#endif + + *flags = p->p_pax; +} + +void +pax_get_flags_td(struct thread *td, pax_flag_t *flags) +{ + + KASSERT(td == curthread, + ("%s: td != curthread", __func__)); + +#ifdef HBSD_DEBUG + struct proc *p; + struct thread *td0; + + p = td->td_proc; + + FOREACH_THREAD_IN_PROC(p, td0) { + KASSERT(td0->td_proc == p, + ("%s: td0->td_proc != p", __func__)); + KASSERT(td0->td_pax == p->p_pax, ("%s: td0->td_pax != p->p_pax", + __func__)); + } +#endif + + *flags = td->td_pax; +} + +void +pax_set_flags(struct proc *p, struct thread *td, const pax_flag_t flags) +{ + struct thread *td0; + + KASSERT(td == curthread, + ("%s: td != curthread", __func__)); + KASSERT(td->td_proc == p, + ("%s: td->td_proc != p", __func__)); + + PROC_LOCK(p); + p->p_pax = flags; + FOREACH_THREAD_IN_PROC(p, td0) { + pax_set_flags_td(td0, flags); + } + PROC_UNLOCK(p); +} + +void +pax_set_flags_td(struct thread *td, const pax_flag_t flags) +{ + + td->td_pax = flags; +} + +/* + * rename to pax_valid_flags, and change return values and type to bool + */ +static int +pax_validate_flags(const pax_flag_t flags) +{ + + if ((flags & ~PAX_NOTE_ALL) != 0) + return (1); + + return (0); +} + +/* + * same as pax_valid_flags + */ +static int +pax_check_conflicting_modes(const pax_flag_t mode) +{ + + if (((mode & PAX_NOTE_ALL_ENABLED) & ((mode & PAX_NOTE_ALL_DISABLED) >> 1)) != 0) + return (1); + + return (0); +} + +/* + * @bried Initialize the new process PaX state + * + * @param imgp Executable image's structure. + * @param mode Requested mode. + * + * @return ENOEXEC on fail + * 0 on success + */ +int +pax_elf(struct image_params *imgp, struct thread *td, pax_flag_t mode) +{ + pax_flag_t flags; + + if (pax_validate_flags(mode) != 0) { + pax_log_internal_imgp(imgp, PAX_LOG_DEFAULT, + "unknown paxflags: %x", mode); + pax_ulog_internal("unknown paxflags: %x\n", mode); + + return (ENOEXEC); + } + + if (pax_check_conflicting_modes(mode) != 0) { + /* + * indicate flags inconsistencies in dmesg and in user terminal + */ + pax_log_internal_imgp(imgp, PAX_LOG_DEFAULT, + "inconsistent paxflags: %x", mode); + pax_ulog_internal("inconsistent paxflags: %x\n", mode); + + return (ENOEXEC); + } + + flags = 0; + +#ifdef PAX_ASLR + flags |= pax_aslr_setup_flags(imgp, td, mode); +#ifdef MAP_32BIT + flags |= pax_disallow_map32bit_setup_flags(imgp, td, mode); +#endif +#endif + + CTR3(KTR_PAX, "%s : flags = %x mode = %x", + __func__, flags, mode); + + /* + * Recheck the flags after the parsing: prevent broken setups. + */ + if (pax_validate_flags(flags) != 0) { + pax_log_internal_imgp(imgp, PAX_LOG_DEFAULT, + "unknown paxflags after the setup: %x", flags); + pax_ulog_internal("unknown paxflags after the setup: %x\n", flags); + + return (ENOEXEC); + } + + /* + * Recheck the flags after the parsing: prevent conflicting setups. + * This check should be always false. + */ + if (pax_check_conflicting_modes(flags) != 0) { + /* + * indicate flags inconsistencies in dmesg and in user terminal + */ + pax_log_internal_imgp(imgp, PAX_LOG_DEFAULT, + "inconsistent paxflags after the setup: %x", flags); + pax_ulog_internal("inconsistent paxflags after the setup: %x\n", flags); + + return (ENOEXEC); + } + + pax_set_flags(imgp->proc, td, flags); + + /* + * if we enable/disable features with secadm, print out a warning + */ + if (mode != 0) { + pax_log_internal_imgp(imgp, PAX_LOG_DEFAULT, + "the process has non-default settings"); + } + + return (0); +} + + +/* + * @brief Print out PaX settings on boot time, and validate some of them. + * + * @return none + */ +static void +pax_sysinit(void) +{ + +} +SYSINIT(pax, SI_SUB_PAX, SI_ORDER_FIRST, pax_sysinit, NULL); + +/* + * @brief Initialize prison's state. + * + * The prison0 state initialized with global state. + * The child prisons state initialized with it's parent's state. + * + * @param pr Initializable prison's pointer. + * + * @return none + */ +void +pax_init_prison(struct prison *pr) +{ + + CTR2(KTR_PAX, "%s: Setting prison %s PaX variables\n", + __func__, pr->pr_name); + + pax_aslr_init_prison(pr); + +#ifdef COMPAT_FREEBSD32 + pax_aslr_init_prison32(pr); +#endif +} + diff --git a/sys/hardenedbsd/hbsd_pax_internal.h b/sys/hardenedbsd/hbsd_pax_internal.h new file mode 100644 index 00000000000..4c2c0f40dd6 --- /dev/null +++ b/sys/hardenedbsd/hbsd_pax_internal.h @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 2016, by Oliver Pinter + * 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$ + */ + +#ifndef __HBSD_PAX_INTERNAL_H +#define __HBSD_PAX_INTERNAL_H + +#define SYSCTL_HBSD_2STATE(g_status, pr_status, parent, name, access, desc) \ + static int sysctl ## parent ## _ ## name (SYSCTL_HANDLER_ARGS); \ + SYSCTL_PROC(parent, OID_AUTO, name, access, \ + NULL, 0, sysctl ## parent ## _ ## name, "I", \ + desc " status: " \ + "0 - disabled, " \ + "1 - enabled"); \ + \ + static int \ + sysctl ## parent ## _ ## name (SYSCTL_HANDLER_ARGS) \ + { \ + struct prison *pr; \ + int err, val; \ + \ + pr = pax_get_prison_td(req->td); \ + \ + val = pr->pr_status; \ + err = sysctl_handle_int(oidp, &val, sizeof(int), req); \ + if (err || (req->newptr == NULL)) \ + return (err); \ + \ + switch (val) { \ + case PAX_FEATURE_SIMPLE_DISABLED: \ + case PAX_FEATURE_SIMPLE_ENABLED: \ + if (pr == &prison0) \ + g_status = val; \ + pr->pr_status = val; \ + break; \ + default: \ + return (EINVAL); \ + } \ + \ + return (0); \ + } + +#define SYSCTL_HBSD_4STATE(g_status, pr_status, parent, name, access) \ + static int sysctl ## parent ## _ ## name (SYSCTL_HANDLER_ARGS); \ + SYSCTL_PROC(parent, OID_AUTO, name, access, \ + NULL, 0, sysctl ## parent ## _ ## name, "I", \ + "Restrictions status: " \ + "0 - disabled, " \ + "1 - opt-in, " \ + "2 - opt-out, " \ + "3 - force enabled"); \ + \ + static int \ + sysctl ## parent ## _ ## name (SYSCTL_HANDLER_ARGS) \ + { \ + struct prison *pr; \ + int err, val; \ + \ + pr = pax_get_prison_td(req->td); \ + \ + val = pr->pr_status; \ + err = sysctl_handle_int(oidp, &val, sizeof(int), req); \ + if (err || (req->newptr == NULL)) \ + return (err); \ + \ + switch (val) { \ + case PAX_FEATURE_DISABLED: \ + case PAX_FEATURE_OPTIN: \ + case PAX_FEATURE_OPTOUT: \ + case PAX_FEATURE_FORCE_ENABLED: \ + if (pr == &prison0) \ + g_status = val; \ + pr->pr_status = val; \ + break; \ + default: \ + return (EINVAL); \ + } \ + \ + return (0); \ + } + +#endif /* __HBSD_PAX_INTERNAL_H */ diff --git a/sys/hardenedbsd/hbsd_pax_log.c b/sys/hardenedbsd/hbsd_pax_log.c new file mode 100644 index 00000000000..39a79db6069 --- /dev/null +++ b/sys/hardenedbsd/hbsd_pax_log.c @@ -0,0 +1,380 @@ +/*- + * Copyright (c) 2014, by Oliver Pinter + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 "opt_pax.h" +#include "opt_ddb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DDB +#include +#endif + +#include "hbsd_pax_internal.h" + +static void pax_log_log(struct proc *p, struct thread *td, pax_log_settings_t flags, + const char *prefix, const char *fmt, va_list ap); +static void pax_log_ulog(const char *prefix, const char *fmt, va_list ap); + +#define PAX_LOG_FEATURES_STRING \ + "\020" \ + "\001PAGEEXEC" \ + "\002NOPAGEEXEC" \ + "\003MPROTECT" \ + "\004NOMPROTECT" \ + "\005SEGVGUARD" \ + "\006NOSEGVGUARD" \ + "\007ASLR" \ + "\010NOASLR" \ + "\011SHLIBRANDOM" \ + "\012NOSHLIBRANDOM" \ + "\013DISALLOWMAP32BIT" \ + "\014NODISALLOWMAP32BIT" \ + "\015" \ + "\016" \ + "\017" \ + "\020" \ + "\021" \ + "\022" \ + "\023" \ + "\024" \ + "\025" \ + "\026" \ + "\027" \ + "\030" \ + "\031" \ + "\032" \ + "\033" \ + "\034" \ + "\035" \ + "\036" \ + "\037" \ + "\040" + +#define __HARDENING_LOG_TEMPLATE(MAIN, SUBJECT, prefix, name) \ +void \ +prefix##_log_##name(struct proc *p, pax_log_settings_t flags, \ + const char* fmt, ...) \ +{ \ + const char *prefix = "["#MAIN" "#SUBJECT"]"; \ + va_list args; \ + \ + if (hardening_log_log == 0) \ + return; \ + \ + va_start(args, fmt); \ + pax_log_log(p, NULL, flags, prefix, fmt, args); \ + va_end(args); \ +} \ + \ +void \ +prefix##_ulog_##name(const char* fmt, ...) \ +{ \ + const char *prefix = "["#MAIN" "#SUBJECT"]"; \ + va_list args; \ + \ + if (hardening_log_ulog == 0) \ + return; \ + \ + va_start(args, fmt); \ + pax_log_ulog(prefix, fmt, args); \ + va_end(args); \ +} + +static int hardening_log_log = PAX_FEATURE_SIMPLE_ENABLED; +static int hardening_log_ulog = PAX_FEATURE_SIMPLE_DISABLED; + +TUNABLE_INT("hardening.log.log", &hardening_log_log); +TUNABLE_INT("hardening.log.ulog", &hardening_log_ulog); + +#ifdef PAX_SYSCTLS +SYSCTL_NODE(_hardening, OID_AUTO, log, CTLFLAG_RD, 0, + "Hardening related logging facility."); + +SYSCTL_HBSD_2STATE(hardening_log_log, pr_hbsd.log.log, _hardening_log, log, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + "log to syslog "); + +SYSCTL_HBSD_2STATE(hardening_log_ulog, pr_hbsd.log.ulog, _hardening_log, ulog, + CTLTYPE_INT|CTLFLAG_RWTUN|CTLFLAG_PRISON|CTLFLAG_SECURE, + "log to syslog "); +#endif + + +static void +hardening_log_sysinit(void) +{ + switch (hardening_log_log) { + case PAX_FEATURE_SIMPLE_DISABLED: + case PAX_FEATURE_SIMPLE_ENABLED: + break; + default: + printf("[HBSD LOG] WARNING, invalid settings in loader.conf!" + " (hardening.log.log = %d)\n", hardening_log_log); + hardening_log_log = PAX_FEATURE_SIMPLE_ENABLED; + } + printf("[HBSD LOG] logging to system: %s\n", + pax_status_simple_str[hardening_log_log]); + + switch (hardening_log_ulog) { + case PAX_FEATURE_SIMPLE_DISABLED: + case PAX_FEATURE_SIMPLE_ENABLED: + break; + default: + printf("[HBSD LOG] WARNING, invalid settings in loader.conf!" + " (hardening.log.ulog = %d)\n", hardening_log_ulog); + hardening_log_ulog = PAX_FEATURE_SIMPLE_ENABLED; + } + printf("[HBSD LOG] logging to user: %s\n", + pax_status_simple_str[hardening_log_ulog]); +} +SYSINIT(hardening_log, SI_SUB_PAX, SI_ORDER_SECOND, hardening_log_sysinit, NULL); + +void +pax_log_init_prison(struct prison *pr) +{ + struct prison *pr_p; + + CTR2(KTR_PAX, "%s: Setting prison %s PaX variables\n", + __func__, pr->pr_name); + + if (pr == &prison0) { + /* prison0 has no parent, use globals */ + pr->pr_hbsd.log.log = hardening_log_log; + pr->pr_hbsd.log.ulog = hardening_log_ulog; + } else { + KASSERT(pr->pr_parent != NULL, + ("%s: pr->pr_parent == NULL", __func__)); + pr_p = pr->pr_parent; + + pr->pr_hbsd.log.log = pr_p->pr_hbsd.log.log; + pr->pr_hbsd.log.ulog = pr_p->pr_hbsd.log.ulog; + } +} + +static void +_pax_log_prefix(struct sbuf *sb, pax_log_settings_t flags, const char *prefix) +{ + + sbuf_printf(sb, "%s ", prefix); +} + +static void +_pax_log_indent(struct sbuf *sb, pax_log_settings_t flags) +{ + + if ((flags & PAX_LOG_NO_INDENT) != PAX_LOG_NO_INDENT) + sbuf_printf(sb, "\n -> "); +} + +static void +_pax_log_proc_details(struct sbuf *sb, pax_log_settings_t flags, struct proc *p) +{ + + if (p != NULL) { + if ((flags & PAX_LOG_P_COMM) == PAX_LOG_P_COMM) + sbuf_printf(sb, "p_comm: %s ", p->p_comm); + + sbuf_printf(sb, "pid: %d ", p->p_pid); + sbuf_printf(sb, "ppid: %d ", p->p_pptr->p_pid); + + if ((flags & PAX_LOG_NO_P_PAX) != PAX_LOG_NO_P_PAX) + sbuf_printf(sb, "p_pax: 0x%b ", p->p_pax, PAX_LOG_FEATURES_STRING); + } +} + +static void +_pax_log_thread_details(struct sbuf *sb, pax_log_settings_t flags, struct thread *td) +{ + + if (td != NULL) { + sbuf_printf(sb, "tid: %d ", td->td_tid); + } +} + +static void +_pax_log_details_end(struct sbuf *sb) +{ + + sbuf_printf(sb, "\n"); +} + +static void +_pax_log_imgp_details(struct sbuf *sb, pax_log_settings_t flags, struct image_params *imgp) +{ + + if (imgp != NULL && imgp->args != NULL) + if (imgp->args->fname != NULL) + sbuf_printf(sb, "fname: %s ", + imgp->args->fname); +} + + +static void +pax_log_log(struct proc *p, struct thread *td, pax_log_settings_t flags, + const char *prefix, const char *fmt, va_list ap) +{ + struct sbuf *sb; + + sb = sbuf_new_auto(); + if (sb == NULL) + panic("%s: Could not allocate memory", __func__); + + _pax_log_prefix(sb, flags, prefix); + + sbuf_vprintf(sb, fmt, ap); + if ((flags & PAX_LOG_SKIP_DETAILS) != PAX_LOG_SKIP_DETAILS) { + _pax_log_indent(sb, flags); + _pax_log_proc_details(sb, flags, p); + _pax_log_thread_details(sb, flags, td); + _pax_log_details_end(sb); + } + + if (sbuf_finish(sb) != 0) + panic("%s: Could not generate message", __func__); + + printf("%s", sbuf_data(sb)); + sbuf_delete(sb); +} + +static void +pax_log_ulog(const char *prefix, const char *fmt, va_list ap) +{ + struct sbuf *sb; + + sb = sbuf_new_auto(); + if (sb == NULL) + panic("%s: Could not allocate memory", __func__); + + if (prefix != NULL) + sbuf_printf(sb, "%s ", prefix); + sbuf_vprintf(sb, fmt, ap); + if (sbuf_finish(sb) != 0) + panic("%s: Could not generate message", __func__); + + hbsd_uprintf("%s", sbuf_data(sb)); \ + sbuf_delete(sb); +} + +void +pax_printf_flags(struct proc *p, pax_log_settings_t flags) +{ + + if (p != NULL) { + printf("pax flags: 0x%b%c", p->p_pax, PAX_LOG_FEATURES_STRING, + ((flags & PAX_LOG_NO_NEWLINE) == PAX_LOG_NO_NEWLINE) ? + ' ' : '\n'); + } +} + +void +pax_printf_flags_td(struct thread *td, pax_log_settings_t flags) +{ + + if (td != NULL) { + printf("pax flags: 0x%b%c", td->td_pax, PAX_LOG_FEATURES_STRING, + ((flags & PAX_LOG_NO_NEWLINE) == PAX_LOG_NO_NEWLINE) ? + ' ' : '\n'); + } +} + +#ifdef DDB +void +pax_db_printf_flags(struct proc *p, pax_log_settings_t flags) +{ + + if (p != NULL) { + db_printf(" pax flags: 0x%b%c", p->p_pax, PAX_LOG_FEATURES_STRING, + ((flags & PAX_LOG_NO_NEWLINE) == PAX_LOG_NO_NEWLINE) ? + ' ' : '\n'); + } +} + +void +pax_db_printf_flags_td(struct thread *td, pax_log_settings_t flags) +{ + + if (td != NULL) { + db_printf(" pax flags: 0x%b%c", td->td_pax, PAX_LOG_FEATURES_STRING, + ((flags & PAX_LOG_NO_NEWLINE) == PAX_LOG_NO_NEWLINE) ? + ' ' : '\n'); + } +} +#endif + +__HARDENING_LOG_TEMPLATE(HBSD, INTERNAL, pax, internal); +__HARDENING_LOG_TEMPLATE(HBSD, ASLR, pax, aslr); + +void +pax_log_internal_imgp(struct image_params *imgp, pax_log_settings_t flags, const char* fmt, ...) +{ + const char *prefix = "[HBSD INTERNAL]"; + struct sbuf *sb; + va_list args; + + KASSERT(imgp != NULL, ("%s: imgp == NULL", __func__)); + + if (hardening_log_log == 0) + return; + + sb = sbuf_new_auto(); + if (sb == NULL) + panic("%s: Could not allocate memory", __func__); + + _pax_log_prefix(sb, flags, prefix); + + va_start(args, fmt); + sbuf_vprintf(sb, fmt, args); + va_end(args); + + if ((flags & PAX_LOG_SKIP_DETAILS) != PAX_LOG_SKIP_DETAILS) { + _pax_log_indent(sb, flags); + _pax_log_imgp_details(sb, flags, imgp); + _pax_log_indent(sb, flags); + _pax_log_proc_details(sb, flags, imgp->proc); + _pax_log_details_end(sb); + } + + if (sbuf_finish(sb) != 0) + panic("%s: Could not generate message", __func__); + + printf("%s", sbuf_data(sb)); + sbuf_delete(sb); +} diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 1a4762d4c26..27472f5ecda 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -373,3 +373,6 @@ device xenpci # Xen HVM Hypervisor services driver # VMware support device vmx # VMware VMXNET3 Ethernet + +options PAX +options PAX_ASLR diff --git a/sys/i386/i386/elf_machdep.c b/sys/i386/i386/elf_machdep.c index 8cd4440cabf..fdc7ea00aa7 100644 --- a/sys/i386/i386/elf_machdep.c +++ b/sys/i386/i386/elf_machdep.c @@ -27,6 +27,7 @@ __FBSDID("$FreeBSD$"); #include "opt_cpu.h" +#include "opt_pax.h" #include #include @@ -34,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -88,6 +90,7 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace, }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 4d7df05ef86..e6a7a8943f7 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -486,11 +486,11 @@ osendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) regs->tf_esp = (int)fp; if (p->p_sysent->sv_sigcode_base != 0) { - regs->tf_eip = p->p_sysent->sv_sigcode_base + szsigcode - + regs->tf_eip = p->p_sigcode_base + szsigcode - szosigcode; } else { /* a.out sysentvec does not use shared page */ - regs->tf_eip = p->p_sysent->sv_psstrings - szosigcode; + regs->tf_eip = p->p_psstrings - szosigcode; } regs->tf_eflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucodesel; @@ -618,7 +618,7 @@ freebsd4_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_esp = (int)sfp; - regs->tf_eip = p->p_sysent->sv_sigcode_base + szsigcode - + regs->tf_eip = p->p_sigcode_base + szsigcode - szfreebsd4_sigcode; regs->tf_eflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucodesel; @@ -792,9 +792,9 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_esp = (int)sfp; - regs->tf_eip = p->p_sysent->sv_sigcode_base; + regs->tf_eip = p->p_sigcode_base; if (regs->tf_eip == 0) - regs->tf_eip = p->p_sysent->sv_psstrings - szsigcode; + regs->tf_eip = p->p_psstrings - szsigcode; regs->tf_eflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; diff --git a/sys/i386/linux/linux_machdep.c b/sys/i386/linux/linux_machdep.c index effc32ae930..c64768ef2b0 100644 --- a/sys/i386/linux/linux_machdep.c +++ b/sys/i386/linux/linux_machdep.c @@ -541,7 +541,7 @@ linux_mmap_common(struct thread *td, l_uintptr_t addr, l_size_t len, l_int prot, * mmap's return value. */ PROC_LOCK(p); - p->p_vmspace->vm_maxsaddr = (char *)USRSTACK - + p->p_vmspace->vm_maxsaddr = (char *)p->p_usrstack - lim_cur(p, RLIMIT_STACK); PROC_UNLOCK(p); } diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c index ab46672b9d8..09aead4dcee 100644 --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -29,6 +29,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -41,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -250,8 +253,8 @@ elf_linux_fixup(register_t **stack_base, struct image_params *imgp) ("unsafe elf_linux_fixup(), should be curproc")); p = imgp->proc; + arginfo = (struct ps_strings *)p->p_psstrings; issetugid = imgp->proc->p_flag & P_SUGID ? 1 : 0; - arginfo = (struct ps_strings *)p->p_sysent->sv_psstrings; uplatform = (Elf32_Addr *)((caddr_t)arginfo - linux_szplatform); args = (Elf32_Auxargs *)imgp->auxargs; pos = *stack_base + (imgp->args->argc + imgp->args->envc + 2); @@ -311,7 +314,7 @@ linux_copyout_strings(struct image_params *imgp) * Also deal with signal trampoline code for this exec type. */ p = imgp->proc; - arginfo = (struct ps_strings *)p->p_sysent->sv_psstrings; + arginfo = (struct ps_strings *)p->p_psstrings; destp = (caddr_t)arginfo - SPARE_USRSPACE - linux_szplatform - roundup((ARG_MAX - imgp->args->stringspace), sizeof(char *)); @@ -975,6 +978,7 @@ struct sysentvec linux_sysvec = { .sv_shared_page_base = LINUX_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = linux_schedtail, + .sv_pax_aslr_init = pax_aslr_init_vmspace, }; INIT_SYSENTVEC(aout_sysvec, &linux_sysvec); diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index e66d67910f9..59d38f32b93 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -33,7 +33,7 @@ __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_compat.h" -#include "opt_core.h" +#include "opt_pax.h" #include #include @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -791,16 +792,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) if (hdr->e_type == ET_DYN) { if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) return (ENOEXEC); - /* - * Honour the base load address from the dso if it is - * non-zero for some reason. - */ - if (baddr == 0) - et_dyn_addr = ET_DYN_LOAD_ADDR; - else - et_dyn_addr = 0; - } else - et_dyn_addr = 0; + } sv = brand_info->sysvec; if (interp != NULL && brand_info->interp_newpath != NULL) newinterp = brand_info->interp_newpath; @@ -821,6 +813,20 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) error = exec_new_vmspace(imgp, sv); imgp->proc->p_sysent = sv; + et_dyn_addr = 0; + if (hdr->e_type == ET_DYN) { + /* + * Honour the base load address from the dso if it is + * non-zero for some reason. + */ + if (baddr == 0) { + et_dyn_addr = ET_DYN_LOAD_ADDR; +#ifdef PAX_ASLR + pax_aslr_execbase(imgp->proc, &et_dyn_addr); +#endif + } + } + vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); if (error) return (error); @@ -918,6 +924,9 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) */ addr = round_page((vm_offset_t)vmspace->vm_daddr + lim_max(imgp->proc, RLIMIT_DATA)); +#ifdef PAX_ASLR + pax_aslr_rtld(imgp->proc, &addr); +#endif PROC_UNLOCK(imgp->proc); imgp->entry_addr = entry; @@ -1011,7 +1020,7 @@ __elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp) } if (imgp->sysent->sv_timekeep_base != 0) { AUXARGS_ENTRY(pos, AT_TIMEKEEP, - imgp->sysent->sv_timekeep_base); + imgp->proc->p_timekeep_base); } AUXARGS_ENTRY(pos, AT_STACKPROT, imgp->sysent->sv_shared_page_obj != NULL && imgp->stack_prot != 0 ? imgp->stack_prot : @@ -1979,9 +1988,9 @@ __elfN(note_procstat_psstrings)(void *arg, struct sbuf *sb, size_t *sizep) KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(ps_strings); #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 - ps_strings = PTROUT(p->p_sysent->sv_psstrings); + ps_strings = PTROUT(p->p_psstrings); #else - ps_strings = p->p_sysent->sv_psstrings; + ps_strings = p->p_psstrings; #endif sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &ps_strings, sizeof(ps_strings)); diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index a7b0526c064..d32d1eeb1b3 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_init_path.h" +#include "opt_pax.h" #include #include @@ -60,6 +61,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -410,6 +412,7 @@ struct sysentvec null_sysvec = { .sv_fetch_syscall_args = null_fetch_syscall_args, .sv_syscallnames = NULL, .sv_schedtail = NULL, + .sv_pax_aslr_init = NULL, }; /* @@ -476,6 +479,11 @@ proc0_init(void *dummy __unused) p->p_flag = P_SYSTEM | P_INMEM; p->p_flag2 = 0; p->p_state = PRS_NORMAL; +#ifdef PAX + p->p_pax = PAX_NOTE_ALL_DISABLED; +#endif + p->p_usrstack = USRSTACK; + p->p_psstrings = PS_STRINGS; knlist_init_mtx(&p->p_klist, &p->p_mtx); STAILQ_INIT(&p->p_ktr); p->p_nice = NZERO; @@ -493,6 +501,9 @@ proc0_init(void *dummy __unused) td->td_flags = TDF_INMEM; td->td_pflags = TDP_KTHREAD; td->td_cpuset = cpuset_thread0(); +#ifdef PAX + td->td_pax = PAX_NOTE_ALL_DISABLED; +#endif prison0_init(); p->p_peers = 0; p->p_leader = p; @@ -713,7 +724,7 @@ start_init(void *dummy) /* * Need just enough stack to hold the faked-up "execve()" arguments. */ - addr = p->p_sysent->sv_usrstack - PAGE_SIZE; + addr = p->p_usrstack - PAGE_SIZE; if (vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &addr, PAGE_SIZE, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0) != 0) panic("init: couldn't allocate argument space"); @@ -740,7 +751,7 @@ start_init(void *dummy) * Move out the boot flag argument. */ options = 0; - ucp = (char *)p->p_sysent->sv_usrstack; + ucp = (char *)p->p_usrstack; (void)subyte(--ucp, 0); /* trailing zero */ if (boothowto & RB_SINGLE) { (void)subyte(--ucp, 's'); diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 5fa62e85c15..c0695f73cab 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include "opt_hwpmc_hooks.h" #include "opt_kdtrace.h" #include "opt_ktrace.h" +#include "opt_pax.h" #include "opt_vm.h" #include @@ -52,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -128,11 +130,6 @@ SYSCTL_INT(_kern, OID_AUTO, disallow_high_osrel, CTLFLAG_RW, &disallow_high_osrel, 0, "Disallow execution of binaries built for higher version of the world"); -static int map_at_zero = 0; -TUNABLE_INT("security.bsd.map_at_zero", &map_at_zero); -SYSCTL_INT(_security_bsd, OID_AUTO, map_at_zero, CTLFLAG_RW, &map_at_zero, 0, - "Permit processes to map an object at virtual address 0."); - static int sysctl_kern_ps_strings(SYSCTL_HANDLER_ARGS) { @@ -143,12 +140,12 @@ sysctl_kern_ps_strings(SYSCTL_HANDLER_ARGS) #ifdef SCTL_MASK32 if (req->flags & SCTL_MASK32) { unsigned int val; - val = (unsigned int)p->p_sysent->sv_psstrings; + val = (unsigned int)p->p_psstrings; error = SYSCTL_OUT(req, &val, sizeof(val)); } else #endif - error = SYSCTL_OUT(req, &p->p_sysent->sv_psstrings, - sizeof(p->p_sysent->sv_psstrings)); + error = SYSCTL_OUT(req, &p->p_psstrings, + sizeof(p->p_psstrings)); return error; } @@ -162,12 +159,12 @@ sysctl_kern_usrstack(SYSCTL_HANDLER_ARGS) #ifdef SCTL_MASK32 if (req->flags & SCTL_MASK32) { unsigned int val; - val = (unsigned int)p->p_sysent->sv_usrstack; + val = (unsigned int)p->p_usrstack; error = SYSCTL_OUT(req, &val, sizeof(val)); } else #endif - error = SYSCTL_OUT(req, &p->p_sysent->sv_usrstack, - sizeof(p->p_sysent->sv_usrstack)); + error = SYSCTL_OUT(req, &p->p_usrstack, + sizeof(p->p_usrstack)); return error; } @@ -452,6 +449,12 @@ interpret: imgp->vp = binvp; } +#ifdef PAX + error = pax_elf(imgp, td, 0); + if (error) + goto exec_fail_dealloc; +#endif + /* * Check file permissions (also 'opens' file) */ @@ -565,6 +568,11 @@ interpret: goto exec_fail_dealloc; } + p->p_psstrings = p->p_sysent->sv_psstrings; +#ifdef PAX_ASLR + pax_aslr_stack_with_gap(p, &(p->p_psstrings)); +#endif + /* * Copy out strings (args and env) and initialize stack base */ @@ -1045,10 +1053,7 @@ exec_new_vmspace(imgp, sv) * not disrupted */ map = &vmspace->vm_map; - if (map_at_zero) - sv_minuser = sv->sv_minuser; - else - sv_minuser = MAX(sv->sv_minuser, PAGE_SIZE); + sv_minuser = MAX(sv->sv_minuser, PAGE_SIZE); if (vmspace->vm_refcnt == 1 && vm_map_min(map) == sv_minuser && vm_map_max(map) == sv->sv_maxuser) { shmexit(vmspace); @@ -1062,19 +1067,44 @@ exec_new_vmspace(imgp, sv) map = &vmspace->vm_map; } +#ifdef PAX_ASLR + PROC_LOCK(imgp->proc); + pax_aslr_init(imgp); + PROC_UNLOCK(imgp->proc); +#endif + /* Map a shared page */ obj = sv->sv_shared_page_obj; if (obj != NULL) { + p->p_shared_page_base = sv->sv_shared_page_base; +#ifdef PAX_ASLR + PROC_LOCK(imgp->proc); + pax_aslr_vdso(p, &(p->p_shared_page_base)); + PROC_UNLOCK(imgp->proc); +#endif vm_object_reference(obj); error = vm_map_fixed(map, obj, 0, - sv->sv_shared_page_base, sv->sv_shared_page_len, + p->p_shared_page_base, sv->sv_shared_page_len, VM_PROT_READ | VM_PROT_EXECUTE, VM_PROT_READ | VM_PROT_EXECUTE, MAP_INHERIT_SHARE | MAP_ACC_NO_CHARGE); if (error) { +#ifdef PAX_ASLR + pax_log_aslr(p, PAX_LOG_DEFAULT, + "failed to map the shared-page @%p", + (void *)p->p_shared_page_base); +#endif vm_object_deallocate(obj); return (error); } + + p->p_timekeep_base = sv->sv_timekeep_base; +#ifdef PAX_ASLR + PROC_LOCK(imgp->proc); + if (p->p_timekeep_base != 0) + pax_aslr_vdso(p, &(p->p_timekeep_base)); + PROC_UNLOCK(imgp->proc); +#endif } /* Allocate a new stack */ @@ -1094,13 +1124,27 @@ exec_new_vmspace(imgp, sv) } else { ssiz = maxssiz; } - stack_addr = sv->sv_usrstack - ssiz; + stack_addr = sv->sv_usrstack; +#ifdef PAX_ASLR + /* Randomize the stack top. */ + pax_aslr_stack(p, &stack_addr); +#endif + /* Save the process specific randomized stack top */ + p->p_usrstack = stack_addr; + /* Calculate the stack's mapping address */ + stack_addr -= ssiz; error = vm_map_stack(map, stack_addr, (vm_size_t)ssiz, obj != NULL && imgp->stack_prot != 0 ? imgp->stack_prot : sv->sv_stackprot, VM_PROT_ALL, MAP_STACK_GROWS_DOWN); - if (error) + if (error) { +#ifdef PAX_ASLR + pax_log_aslr(p, PAX_LOG_DEFAULT, + "failed to map the main stack @%p", + (void *)p->p_usrstack); +#endif return (error); + } #ifdef __ia64__ /* Allocate a new register stack */ @@ -1278,10 +1322,15 @@ exec_copyout_strings(imgp) execpath_len = 0; p = imgp->proc; szsigcode = 0; - arginfo = (struct ps_strings *)p->p_sysent->sv_psstrings; - if (p->p_sysent->sv_sigcode_base == 0) { + p->p_sigcode_base = p->p_sysent->sv_sigcode_base; + arginfo = (struct ps_strings *)p->p_psstrings; + if (p->p_sigcode_base == 0) { if (p->p_sysent->sv_szsigcode != NULL) szsigcode = *(p->p_sysent->sv_szsigcode); +#ifdef PAX_ASLR + } else { + pax_aslr_vdso(p, &(p->p_sigcode_base)); +#endif } destp = (uintptr_t)arginfo; diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 37774efff14..b3c1722e13e 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -481,6 +481,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, __rangeof(struct thread, td_startcopy, td_endcopy)); bcopy(&p2->p_comm, &td2->td_name, sizeof(td2->td_name)); + td2->td_pax = p2->p_pax; td2->td_sigstk = td->td_sigstk; td2->td_flags = TDF_INMEM; td2->td_lend_user_pri = PRI_MAX; diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index af5feb35517..8029e79cb0c 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_pax.h" #include #include @@ -249,6 +250,10 @@ prison0_init(void) prison0.pr_cpuset = cpuset_ref(thread0.td_cpuset); prison0.pr_osreldate = osreldate; strlcpy(prison0.pr_osrelease, osrelease, sizeof(prison0.pr_osrelease)); + +#ifdef PAX + pax_init_prison(&prison0); +#endif } #ifdef INET @@ -1367,6 +1372,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) goto done_releroot; } +#ifdef PAX + pax_init_prison(pr); +#endif + mtx_lock(&pr->pr_mtx); /* * New prisons do not yet have a reference, because we do not @@ -4731,6 +4740,27 @@ db_show_prison(struct prison *pr) ii == 0 ? "ip6.addr =" : " ", ip6_sprintf(ip6buf, &pr->pr_ip6[ii])); #endif +#ifdef PAX + db_printf(" pr_hbsd = {\n"); + + db_printf(" .aslr = {\n"); + db_printf(" .status = %d\n", + pr->pr_hbsd.aslr.status); + db_printf(" .compat_status = %d\n", + pr->pr_hbsd.aslr.compat_status); + db_printf(" .disallow_map32bit_status = %d\n", + pr->pr_hbsd.aslr.disallow_map32bit_status); + db_printf(" }\n"); + + db_printf(" .log = {\n"); + db_printf(" .log = %d\n", + pr->pr_hbsd.log.log); + db_printf(" .ulog = %d\n", + pr->pr_hbsd.log.ulog); + db_printf(" }\n"); + + db_printf(" }\n"); +#endif } DB_SHOW_COMMAND(prison, db_show_prison_command) diff --git a/sys/kern/kern_mib.c b/sys/kern/kern_mib.c index 030779138d9..c0da327a14a 100644 --- a/sys/kern/kern_mib.c +++ b/sys/kern/kern_mib.c @@ -82,6 +82,8 @@ SYSCTL_ROOT_NODE(OID_AUTO, compat, CTLFLAG_RW, 0, "Compatibility code"); SYSCTL_ROOT_NODE(OID_AUTO, security, CTLFLAG_RW, 0, "Security"); +SYSCTL_ROOT_NODE(OID_AUTO, hardening, CTLFLAG_RW, 0, + "Kernel hardening features"); #ifdef REGRESSION SYSCTL_ROOT_NODE(OID_AUTO, regression, CTLFLAG_RW, 0, "Regression test MIB"); diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 4f103b24c02..89e84ec766e 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -1541,7 +1541,7 @@ get_proc_vector32(struct thread *td, struct proc *p, char ***proc_vectorp, size_t vsize, size; int i, error; - error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings), + error = proc_read_mem(td, p, (vm_offset_t)(p->p_psstrings), &pss, sizeof(pss)); if (error != 0) return (error); @@ -1617,7 +1617,7 @@ get_proc_vector(struct thread *td, struct proc *p, char ***proc_vectorp, if (SV_PROC_FLAG(p, SV_ILP32) != 0) return (get_proc_vector32(td, p, proc_vectorp, vsizep, type)); #endif - error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings), + error = proc_read_mem(td, p, (vm_offset_t)(p->p_psstrings), &pss, sizeof(pss)); if (error != 0) return (error); @@ -2609,13 +2609,13 @@ sysctl_kern_proc_ps_strings(SYSCTL_HANDLER_ARGS) * process. */ ps_strings32 = SV_PROC_FLAG(p, SV_ILP32) != 0 ? - PTROUT(p->p_sysent->sv_psstrings) : 0; + PTROUT(p->p_psstrings) : 0; PROC_UNLOCK(p); error = SYSCTL_OUT(req, &ps_strings32, sizeof(ps_strings32)); return (error); } #endif - ps_strings = p->p_sysent->sv_psstrings; + ps_strings = p->p_psstrings; PROC_UNLOCK(p); error = SYSCTL_OUT(req, &ps_strings, sizeof(ps_strings)); return (error); @@ -2719,13 +2719,13 @@ sysctl_kern_proc_sigtramp(SYSCTL_HANDLER_ARGS) bzero(&kst32, sizeof(kst32)); if (SV_PROC_FLAG(p, SV_ILP32)) { if (sv->sv_sigcode_base != 0) { - kst32.ksigtramp_start = sv->sv_sigcode_base; - kst32.ksigtramp_end = sv->sv_sigcode_base + + kst32.ksigtramp_start = p->p_sigcode_base; + kst32.ksigtramp_end = p->p_sigcode_base + *sv->sv_szsigcode; } else { - kst32.ksigtramp_start = sv->sv_psstrings - + kst32.ksigtramp_start = p->p_psstrings - *sv->sv_szsigcode; - kst32.ksigtramp_end = sv->sv_psstrings; + kst32.ksigtramp_end = p->p_psstrings; } } PROC_UNLOCK(p); @@ -2735,13 +2735,13 @@ sysctl_kern_proc_sigtramp(SYSCTL_HANDLER_ARGS) #endif bzero(&kst, sizeof(kst)); if (sv->sv_sigcode_base != 0) { - kst.ksigtramp_start = (char *)sv->sv_sigcode_base; - kst.ksigtramp_end = (char *)sv->sv_sigcode_base + + kst.ksigtramp_start = (char *)p->p_sigcode_base; + kst.ksigtramp_end = (char *)p->p_sigcode_base + *sv->sv_szsigcode; } else { - kst.ksigtramp_start = (char *)sv->sv_psstrings - + kst.ksigtramp_start = (char *)p->p_psstrings - *sv->sv_szsigcode; - kst.ksigtramp_end = (char *)sv->sv_psstrings; + kst.ksigtramp_end = (char *)p->p_psstrings; } PROC_UNLOCK(p); error = SYSCTL_OUT(req, &kst, sizeof(kst)); diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c index b8b8feeb1e4..716ed2b1bef 100644 --- a/sys/kern/kern_resource.c +++ b/sys/kern/kern_resource.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" +#include "opt_pax.h" #include #include @@ -47,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -776,12 +778,12 @@ kern_proc_setrlimit(struct thread *td, struct proc *p, u_int which, if (limp->rlim_cur > oldssiz.rlim_cur) { prot = p->p_sysent->sv_stackprot; size = limp->rlim_cur - oldssiz.rlim_cur; - addr = p->p_sysent->sv_usrstack - + addr = p->p_usrstack - limp->rlim_cur; } else { prot = VM_PROT_NONE; size = oldssiz.rlim_cur - limp->rlim_cur; - addr = p->p_sysent->sv_usrstack - + addr = p->p_usrstack - oldssiz.rlim_cur; } addr = trunc_page(addr); diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index dd119a213ca..7c7626f93d3 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -255,6 +255,7 @@ create_thread(struct thread *td, mcontext_t *ctx, td->td_proc->p_flag |= P_HADTHREADS; thread_link(newtd, p); bcopy(p->p_comm, newtd->td_name, sizeof(newtd->td_name)); + newtd->td_pax = p->p_pax; thread_lock(td); /* let the scheduler know about these things. */ sched_fork_thread(td, newtd); diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c index ebc08e73d8d..979bd6c1814 100644 --- a/sys/kern/subr_prf.c +++ b/sys/kern/subr_prf.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #ifdef _KERNEL #include "opt_ddb.h" +#include "opt_pax.h" #include "opt_printf.h" #endif /* _KERNEL */ @@ -52,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -180,6 +182,49 @@ uprintf(const char *fmt, ...) return (retval); } +int +hbsd_uprintf(const char *fmt, ...) +{ + va_list ap; + struct putchar_arg pca; + struct proc *p; + struct thread *td; + int p_locked, retval; + + td = curthread; + if (TD_IS_IDLETHREAD(td)) + return (0); + + sx_slock(&proctree_lock); + p = td->td_proc; + if ((p_locked = PROC_LOCKED(p))) + PROC_LOCK(p); + if ((p->p_flag & P_CONTROLT) == 0) { + if (p_locked) + PROC_UNLOCK(p); + sx_sunlock(&proctree_lock); + return (0); + } + SESS_LOCK(p->p_session); + pca.tty = p->p_session->s_ttyp; + SESS_UNLOCK(p->p_session); + if (p_locked) + PROC_UNLOCK(p); + if (pca.tty == NULL) { + sx_sunlock(&proctree_lock); + return (0); + } + pca.flags = TOTTY; + pca.p_bufr = NULL; + va_start(ap, fmt); + tty_lock(pca.tty); + sx_sunlock(&proctree_lock); + retval = kvprintf(fmt, putchar, &pca, 10, ap); + tty_unlock(pca.tty); + va_end(ap); + return (retval); +} + /* * tprintf and vtprintf print on the controlling terminal associated with the * given session, possibly to the log as well. diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 09a43f4bfc7..89cc586fb37 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" +#include "opt_pax.h" #include #include @@ -44,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include diff --git a/sys/mips/mips/elf_machdep.c b/sys/mips/mips/elf_machdep.c index d37471388ae..97b7492f4b2 100644 --- a/sys/mips/mips/elf_machdep.c +++ b/sys/mips/mips/elf_machdep.c @@ -28,6 +28,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include @@ -36,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -83,6 +86,7 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace, }; static Elf64_Brandinfo freebsd_brand_info = { @@ -139,6 +143,7 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace, }; static Elf32_Brandinfo freebsd_brand_info = { diff --git a/sys/mips/mips/freebsd32_machdep.c b/sys/mips/mips/freebsd32_machdep.c index 9de4685bac8..ddb1f8da787 100644 --- a/sys/mips/mips/freebsd32_machdep.c +++ b/sys/mips/mips/freebsd32_machdep.c @@ -31,6 +31,7 @@ */ #include "opt_compat.h" +#include "opt_pax.h" #define __ELF_WORD_SIZE 32 @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +108,7 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = freebsd32_syscallnames, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace32, }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); @@ -464,7 +467,7 @@ freebsd32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) /* * Signal trampoline code is at base of user stack. */ - td->td_frame->ra = (register_t)(intptr_t)FREEBSD32_PS_STRINGS - *(p->p_sysent->sv_szsigcode); + td->td_frame->ra = (register_t)(intptr_t)p->p_psstrings - *(p->p_sysent->sv_szsigcode); PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } diff --git a/sys/mips/mips/pm_machdep.c b/sys/mips/mips/pm_machdep.c index 7db671ddb25..5f41ab7d286 100644 --- a/sys/mips/mips/pm_machdep.c +++ b/sys/mips/mips/pm_machdep.c @@ -179,7 +179,7 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) /* * Signal trampoline code is at base of user stack. */ - regs->ra = (register_t)(intptr_t)PS_STRINGS - *(p->p_sysent->sv_szsigcode); + regs->ra = (register_t)(intptr_t)p->p_psstrings - *(p->p_sysent->sv_szsigcode); PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } diff --git a/sys/powerpc/powerpc/elf32_machdep.c b/sys/powerpc/powerpc/elf32_machdep.c index dbe58df1573..532888dbb28 100644 --- a/sys/powerpc/powerpc/elf32_machdep.c +++ b/sys/powerpc/powerpc/elf32_machdep.c @@ -25,6 +25,8 @@ * $FreeBSD$ */ +#include "opt_pax.h" + #include #include #include @@ -34,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +110,7 @@ struct sysentvec elf32_freebsd_sysvec = { .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace32, }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); diff --git a/sys/powerpc/powerpc/elf64_machdep.c b/sys/powerpc/powerpc/elf64_machdep.c index 0c41a8decb1..1e7838b1b36 100644 --- a/sys/powerpc/powerpc/elf64_machdep.c +++ b/sys/powerpc/powerpc/elf64_machdep.c @@ -25,12 +25,15 @@ * $FreeBSD$ */ +#include "opt_pax.h" + #include #include #include #include #include #include +#include #include #include #include @@ -83,6 +86,7 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace, }; INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); diff --git a/sys/powerpc/powerpc/exec_machdep.c b/sys/powerpc/powerpc/exec_machdep.c index 691cf2ddc16..4b5377fd94f 100644 --- a/sys/powerpc/powerpc/exec_machdep.c +++ b/sys/powerpc/powerpc/exec_machdep.c @@ -295,7 +295,7 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); - tf->srr0 = (register_t)p->p_sysent->sv_sigcode_base; + tf->srr0 = (register_t)p->p_sigcode_base; /* * copy the frame out to userland. diff --git a/sys/sparc64/sparc64/elf_machdep.c b/sys/sparc64/sparc64/elf_machdep.c index 4d5571781d1..394d6b87fb4 100644 --- a/sys/sparc64/sparc64/elf_machdep.c +++ b/sys/sparc64/sparc64/elf_machdep.c @@ -31,6 +31,8 @@ * from: NetBSD: mdreloc.c,v 1.42 2008/04/28 20:23:04 martin Exp */ +#include "opt_pax.h" + #include __FBSDID("$FreeBSD$"); @@ -40,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -87,6 +90,7 @@ static struct sysentvec elf64_freebsd_sysvec = { .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, + .sv_pax_aslr_init = pax_aslr_init_vmspace, }; static Elf64_Brandinfo freebsd_brand_info = { diff --git a/sys/sparc64/sparc64/machdep.c b/sys/sparc64/sparc64/machdep.c index f016eaa7226..0b6fa5bca65 100644 --- a/sys/sparc64/sparc64/machdep.c +++ b/sys/sparc64/sparc64/machdep.c @@ -1003,7 +1003,7 @@ exec_setregs(struct thread *td, struct image_params *imgp, u_long stack) bzero(pcb, sizeof(*pcb)); bzero(tf, sizeof(*tf)); tf->tf_out[0] = stack; - tf->tf_out[3] = p->p_sysent->sv_psstrings; + tf->tf_out[3] = p->p_psstrings; tf->tf_out[6] = sp - SPOFF - sizeof(struct frame); tf->tf_tnpc = imgp->entry_addr + 4; tf->tf_tpc = imgp->entry_addr; diff --git a/sys/sys/jail.h b/sys/sys/jail.h index cfe71d8f22c..af0e589700b 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -30,6 +30,10 @@ #ifndef _SYS_JAIL_H_ #define _SYS_JAIL_H_ +#if defined(_KERNEL) || defined(_WANT_PRISON) +#include +#endif + #ifdef _KERNEL struct jail_v0 { u_int32_t version; @@ -187,6 +191,7 @@ struct prison { char pr_domainname[MAXHOSTNAMELEN]; /* (p) jail domainname */ char pr_hostuuid[HOSTUUIDLEN]; /* (p) jail hostuuid */ char pr_osrelease[OSRELEASELEN]; /* (c) kern.osrelease value */ + struct hbsd_features pr_hbsd; /* (p) PaX-inspired hardening features */ }; struct prison_racct { diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h index 68e0858ac61..16ac25b12ac 100644 --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -104,6 +104,7 @@ enum sysinit_sub_id { SI_SUB_WITNESS = 0x1A80000, /* witness initialization */ SI_SUB_MTX_POOL_DYNAMIC = 0x1AC0000, /* dynamic mutex pool */ SI_SUB_LOCK = 0x1B00000, /* various locks */ + SI_SUB_PAX = 0x1B80000, /* pax setup */ SI_SUB_EVENTHANDLER = 0x1C00000, /* eventhandler init */ SI_SUB_VNET_PRELINK = 0x1E00000, /* vnet init before modules */ SI_SUB_KLD = 0x2000000, /* KLD and module setup */ diff --git a/sys/sys/pax.h b/sys/sys/pax.h new file mode 100644 index 00000000000..2c696719fa0 --- /dev/null +++ b/sys/sys/pax.h @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 2006 Elad Efrat + * Copyright (c) 2013-2016, by Oliver Pinter + * Copyright (c) 2014-2015 by Shawn Webb + * 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$ + */ + +#ifndef _SYS_PAX_H +#define _SYS_PAX_H + +#if defined(_KERNEL) || defined(_WANT_PRISON) +struct hbsd_features { + struct hbsd_aslr { + int status; /* (p) PaX ASLR enabled */ + int compat_status; /* (p) PaX ASLR enabled (compat32) */ + int disallow_map32bit_status; /* (p) MAP_32BIT protection (__LP64__ only) */ + } aslr; + struct hbsd_log { + int log; /* (p) Per-jail logging status */ + int ulog; /* (p) Per-jail user visible logging status */ + } log; +}; +#endif + +#ifdef _KERNEL + +#include + +struct image_params; +struct prison; +struct thread; +struct proc; +struct vnode; +struct vm_offset_t; + +typedef uint32_t pax_flag_t; + +/* + * used in sysctl handler + */ +#define PAX_FEATURE_DISABLED 0 +#define PAX_FEATURE_OPTIN 1 +#define PAX_FEATURE_OPTOUT 2 +#define PAX_FEATURE_FORCE_ENABLED 3 + +extern const char *pax_status_str[]; + +#define PAX_FEATURE_SIMPLE_DISABLED 0 +#define PAX_FEATURE_SIMPLE_ENABLED 1 + +extern const char *pax_status_simple_str[]; + +/* + * generic pax functions + */ +int pax_elf(struct image_params *imgp, struct thread *td, pax_flag_t mode); +void pax_get_flags(struct proc *p, pax_flag_t *flags); +void pax_get_flags_td(struct thread *td, pax_flag_t *flags); +struct prison *pax_get_prison(struct proc *p); +struct prison *pax_get_prison_td(struct thread *td); +void pax_init_prison(struct prison *pr); + +/* + * ASLR related functions + */ +bool pax_aslr_active(struct proc *p); +#ifdef PAX_ASLR +void pax_aslr_init_prison(struct prison *pr); +void pax_aslr_init_prison32(struct prison *pr); +void pax_aslr_init_vmspace(struct proc *p); +void pax_aslr_init_vmspace32(struct proc *p); +#else +#define pax_aslr_init_prison(pr) do {} while (0) +#define pax_aslr_init_prison32(pr) do {} while (0) +#define pax_aslr_init_vmspace NULL +#define pax_aslr_init_vmspace32 NULL +#endif +void pax_aslr_init(struct image_params *imgp); +void pax_aslr_execbase(struct proc *p, u_long *et_dyn_addrp); +void pax_aslr_mmap(struct proc *p, vm_offset_t *addr, vm_offset_t orig_addr, int mmap_flags); +void pax_aslr_mmap_map_32bit(struct proc *p, vm_offset_t *addr, vm_offset_t orig_addr, int mmap_flags); +void pax_aslr_rtld(struct proc *p, u_long *addr); +pax_flag_t pax_aslr_setup_flags(struct image_params *imgp, struct thread *td, pax_flag_t mode); +void pax_aslr_stack(struct proc *p, vm_offset_t *addr); +void pax_aslr_stack_with_gap(struct proc *p, vm_offset_t *addr); +void pax_aslr_vdso(struct proc *p, vm_offset_t *addr); +pax_flag_t pax_disallow_map32bit_setup_flags(struct image_params *imgp, struct thread *td, pax_flag_t mode); +bool pax_disallow_map32bit_active(struct thread *td, int mmap_flags); + +/* + * Log related functions + */ + +typedef uint64_t pax_log_settings_t; + +#define PAX_LOG_DEFAULT 0x00000000 +#define PAX_LOG_SKIP_DETAILS 0x00000001 +#define PAX_LOG_NO_NEWLINE 0x00000002 +#define PAX_LOG_P_COMM 0x00000004 +#define PAX_LOG_NO_P_PAX 0x00000008 +#define PAX_LOG_NO_INDENT 0x00000010 + +void pax_log_init_prison(struct prison *pr); +void pax_printf_flags(struct proc *p, pax_log_settings_t flags); +void pax_printf_flags_td(struct thread *td, pax_log_settings_t flags); +void pax_db_printf_flags(struct proc *p, pax_log_settings_t flags); +void pax_db_printf_flags_td(struct thread *td, pax_log_settings_t flags); +int hbsd_uprintf(const char *fmt, ...) __printflike(1, 2); +void pax_log_internal(struct proc *, pax_log_settings_t flags, const char *fmt, ...) __printflike(3, 4); +void pax_log_internal_imgp(struct image_params *imgp, pax_log_settings_t flags, const char* fmt, ...) __printflike(3, 4); +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); + +/* + * Hardening related functions + */ +#ifdef PAX_HARDENING +void pax_hardening_init_prison(struct prison *pr); +#else +#define pax_hardening_init_prison(pr) do {} while (0) +#endif +int pax_procfs_harden(struct thread *td); + +#define PAX_NOTE_PAGEEXEC 0x00000001 +#define PAX_NOTE_NOPAGEEXEC 0x00000002 +#define PAX_NOTE_MPROTECT 0x00000004 +#define PAX_NOTE_NOMPROTECT 0x00000008 +#define PAX_NOTE_SEGVGUARD 0x00000010 +#define PAX_NOTE_NOSEGVGUARD 0x00000020 +#define PAX_NOTE_ASLR 0x00000040 +#define PAX_NOTE_NOASLR 0x00000080 +#define PAX_NOTE_SHLIBRANDOM 0x00000100 +#define PAX_NOTE_NOSHLIBRANDOM 0x00000200 +#define PAX_NOTE_DISALLOWMAP32BIT 0x00000400 +#define PAX_NOTE_NODISALLOWMAP32BIT 0x00000800 + +#define PAX_NOTE_RESERVED0 0x40000000 +#define PAX_NOTE_FINALIZED 0x80000000 + +#define PAX_NOTE_ALL_ENABLED \ + (PAX_NOTE_PAGEEXEC | PAX_NOTE_MPROTECT | PAX_NOTE_SEGVGUARD | \ + PAX_NOTE_ASLR | PAX_NOTE_SHLIBRANDOM | PAX_NOTE_DISALLOWMAP32BIT) +#define PAX_NOTE_ALL_DISABLED \ + (PAX_NOTE_NOPAGEEXEC | PAX_NOTE_NOMPROTECT | \ + PAX_NOTE_NOSEGVGUARD | PAX_NOTE_NOASLR | PAX_NOTE_NOSHLIBRANDOM | \ + PAX_NOTE_NODISALLOWMAP32BIT) +#define PAX_NOTE_ALL (PAX_NOTE_ALL_ENABLED | PAX_NOTE_ALL_DISABLED | PAX_NOTE_FINALIZED) + +#endif /* _KERNEL */ + +#define PAX_HARDENING_SHLIBRANDOM 0x00000100 +#define PAX_HARDENING_NOSHLIBRANDOM 0x00000200 + +#endif /* !_SYS_PAX_H */ diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 910d8d14271..2f6daa4c594 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -286,6 +286,7 @@ struct thread { u_char td_pri_class; /* (t) Scheduling class. */ u_char td_user_pri; /* (t) User pri from estcpu and nice. */ u_char td_base_user_pri; /* (t) Base user pri */ + uint32_t td_pax; /* (b) Cached PaX settings from process. */ #define td_endcopy td_pcb /* @@ -561,6 +562,12 @@ struct proc { rlim_t p_cpulimit; /* (c) Current CPU limit in seconds. */ signed char p_nice; /* (c) Process "nice" value. */ int p_fibnum; /* in this routing domain XXX MRT */ + uint32_t p_pax; /* (b) PaX is enabled to this process */ + vm_offset_t p_usrstack; /* (b) Process stack top. */ + vm_offset_t p_psstrings; /* (b) Process psstrings address. */ + vm_offset_t p_timekeep_base; /* (c) Address of timekeep structure. */ + vm_offset_t p_shared_page_base; /* (c) Address of shared page. */ + vm_offset_t p_sigcode_base; /* Address of sigcode. */ /* End area that is copied on creation. */ #define p_endcopy p_xstat diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 908dd431c68..657ecc109d4 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -753,6 +753,7 @@ SYSCTL_DECL(_compat); SYSCTL_DECL(_regression); SYSCTL_DECL(_security); SYSCTL_DECL(_security_bsd); +SYSCTL_DECL(_hardening); extern char machine[]; extern char osrelease[]; diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index c49db41844a..f9f98536fc4 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -38,6 +38,7 @@ struct rlimit; struct sysent; struct thread; struct ksiginfo; +struct proc; typedef int sy_call_t(struct thread *, void *); @@ -130,6 +131,7 @@ struct sysentvec { uint32_t sv_timekeep_gen; void *sv_shared_page_obj; void (*sv_schedtail)(struct thread *); + void (* const sv_pax_aslr_init)(struct proc *p); }; #define SV_ILP32 0x000100 diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index 0a93d22bc11..9a7717ed234 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -65,12 +65,15 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_pax.h" + #include #include #include #include #include #include +#include #include #include #include @@ -295,6 +298,15 @@ vmspace_alloc(vm_offset_t min, vm_offset_t max, pmap_pinit_t pinit) vm->vm_taddr = 0; vm->vm_daddr = 0; vm->vm_maxsaddr = 0; +#ifdef PAX_ASLR + vm->vm_aslr_delta_mmap = 0; + vm->vm_aslr_delta_stack = 0; + vm->vm_aslr_delta_exec = 0; + vm->vm_aslr_delta_vdso = 0; +#ifdef __LP64__ + vm->vm_aslr_delta_map32bit = 0; +#endif +#endif return (vm); } @@ -3275,6 +3287,15 @@ vmspace_fork(struct vmspace *vm1, vm_ooffset_t *fork_charge) vm2->vm_taddr = vm1->vm_taddr; vm2->vm_daddr = vm1->vm_daddr; vm2->vm_maxsaddr = vm1->vm_maxsaddr; +#ifdef PAX_ASLR + vm2->vm_aslr_delta_exec = vm1->vm_aslr_delta_exec; + vm2->vm_aslr_delta_mmap = vm1->vm_aslr_delta_mmap; + vm2->vm_aslr_delta_stack = vm1->vm_aslr_delta_stack; + vm2->vm_aslr_delta_vdso = vm1->vm_aslr_delta_vdso; +#ifdef __LP64__ + vm2->vm_aslr_delta_map32bit = vm1->vm_aslr_delta_map32bit; +#endif +#endif vm_map_lock(old_map); if (old_map->busy) vm_map_wait_busy(old_map); @@ -3657,7 +3678,8 @@ Retry: return (KERN_NO_SPACE); } - is_procstack = (addr >= (vm_offset_t)vm->vm_maxsaddr) ? 1 : 0; + is_procstack = (addr >= (vm_offset_t)vm->vm_maxsaddr && + addr < (vm_offset_t)p->p_usrstack) ? 1 : 0; /* * If this is the main process stack, see if we're over the stack diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h index 8cced057ea1..04e8fe5bcb4 100644 --- a/sys/vm/vm_map.h +++ b/sys/vm/vm_map.h @@ -241,6 +241,13 @@ struct vmspace { caddr_t vm_taddr; /* (c) user virtual address of text */ caddr_t vm_daddr; /* (c) user virtual address of data */ caddr_t vm_maxsaddr; /* user VA at max stack growth */ + vm_offset_t vm_aslr_delta_mmap; /* mmap() random delta for ASLR */ + vm_offset_t vm_aslr_delta_stack; /* stack random delta for ASLR */ + vm_offset_t vm_aslr_delta_exec; /* exec base random delta for ASLR */ + vm_offset_t vm_aslr_delta_vdso; /* VDSO base random delta for ASLR */ +#ifdef __LP64__ + vm_offset_t vm_aslr_delta_map32bit; /* random for MAP_32BIT mappings */ +#endif volatile int vm_refcnt; /* number of references */ /* * Keep the PMAP last, so that CPU-specific variations of that diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index b4bf117962e..e87bc4d3486 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_hwpmc_hooks.h" +#include "opt_pax.h" #include #include @@ -54,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -211,6 +213,9 @@ sys_mmap(td, uap) off_t pos; struct vmspace *vms = td->td_proc->p_vmspace; cap_rights_t rights; +#ifdef PAX_ASLR + int pax_aslr_done; +#endif addr = (vm_offset_t) uap->addr; size = uap->len; @@ -220,6 +225,10 @@ sys_mmap(td, uap) fp = NULL; +#ifdef PAX_ASLR + pax_aslr_done = 0; +#endif + /* * Enforce the constraints. * Mapping of length 0 is only allowed for old binaries. @@ -297,7 +306,13 @@ sys_mmap(td, uap) */ if (addr + size > MAP_32BIT_MAX_ADDR) addr = 0; -#endif +#ifdef PAX_ASLR + PROC_LOCK(td->td_proc); + pax_aslr_mmap_map_32bit(td->td_proc, &addr, (vm_offset_t)uap->addr, flags); + pax_aslr_done = 1; + PROC_UNLOCK(td->td_proc); +#endif /* PAX_ASLR */ +#endif /* MAP_32BIT */ } else { /* * XXX for non-fixed mappings where no hint is provided or @@ -315,6 +330,12 @@ sys_mmap(td, uap) addr = round_page((vm_offset_t)vms->vm_daddr + lim_max(td->td_proc, RLIMIT_DATA)); PROC_UNLOCK(td->td_proc); +#ifdef PAX_ASLR + PROC_LOCK(td->td_proc); + pax_aslr_mmap(td->td_proc, &addr, (vm_offset_t)uap->addr, flags); + pax_aslr_done = 1; + PROC_UNLOCK(td->td_proc); +#endif } if (flags & MAP_ANON) { /*