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) { /*