diff --git a/UPDATING b/UPDATING --- a/UPDATING +++ b/UPDATING @@ -27,6 +27,13 @@ world, or to merely disable the most expensive debugging functionality at runtime, run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) +20220915: + The behavior of the kern.elf32.allow_wx and kern.elf64.allow_wx + sysctls and tunables has changed. To enforce W^X, a user must now + also set the vm.enforce_wxorx sysctl and/or tunable to a non-zero + value. The kern.elf32.allow_wx and kern.elf64.allow_wx sysctls now + default to false. + 20220610: LinuxKPI pm.h changes require an update to the latest drm-kmod version before re-compiling to avoid errors. diff --git a/share/man/man7/security.7 b/share/man/man7/security.7 --- a/share/man/man7/security.7 +++ b/share/man/man7/security.7 @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 14, 2022 +.Dd September 15, 2022 .Dt SECURITY 7 .Os .Sh NAME @@ -1082,9 +1082,44 @@ .It Dv kern.elf32.allow_wx Enables mapping of simultaneously writable and executable pages for 32-bit processes. +In general, it is considered less secure to allow executable pages to +be simultaneously writable. +Not all systems are capable of enforcing execution permissions. .It Dv kern.elf64.allow_wx Enables mapping of simultaneously writable and executable pages for 64-bit processes. +In general, it is considered less secure to allow executable pages to +be simultaneously writable. +Not all systems are capable of enforcing execution permissions. +.It Dv vm.enforce_wxorx +Controls how the system enforces protections against simultaneously writable +and executable pages for user processes where such enforcement has been +requested (for example, through the +.Va kern.elf32.allow_wx +or +.Va kern.elf64.allow_wx +sysctls and/or tunables). +.Pp +Recognized values are: +.Bl -tag -width flag +.It Ic 0 +Do not enforce W^X permissions. +This is the default initial value. +.It Ic 1 +Log page mappings which are simultaneously writable and executable, but allow +them to proceed anyway. +.It Ic 2 +Block (and log) page mappings which are simultaneously writable and executable. +.It Ic 3 +Send a +.Dv SIGSEGV +signal to any process which attempts to map memory which is simultaneously +writable and executable. +This may be useful for debugging code paths which request these memory +mappings. +.El +.Pp +Not all systems are capable of enforcing execution permissions. .El .Sh SEE ALSO .Xr chflags 1 , diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -219,7 +220,7 @@ CTLFLAG_RWTUN, &__elfN(sigfastblock), 0, "enable sigfastblock for new processes"); -static bool __elfN(allow_wx) = true; +static bool __elfN(allow_wx) = false; SYSCTL_BOOL(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, allow_wx, CTLFLAG_RWTUN, &__elfN(allow_wx), 0, "Allow pages to be mapped simultaneously writable and executable"); @@ -744,6 +745,15 @@ /* Loadable segment */ prot = __elfN(trans_prot)(phdr[i].p_flags); + if ((imgp->map_flags & MAP_WXORX) == MAP_WXORX && + (prot & (VM_PROT_WRITE | VM_PROT_EXECUTE)) == + (VM_PROT_WRITE | VM_PROT_EXECUTE)) { + log(LOG_WARNING, + "Refusing to run ELF binary with WX section (%s)\n", + imgp->execpath != NULL ? imgp->execpath : + ""); + return (ENOEXEC); + } error = __elfN(load_section)(imgp, phdr[i].p_offset, (caddr_t)(uintptr_t)phdr[i].p_vaddr + rbase, phdr[i].p_memsz, phdr[i].p_filesz, prot); diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -82,7 +82,9 @@ #include #include #include +#include #include +#include #include #include @@ -1600,6 +1602,54 @@ return (FALSE); } +static int enforce_wxorx = 0; +SYSCTL_INT(_vm, OID_AUTO, enforce_wxorx, CTLFLAG_RWTUN, + &enforce_wxorx, 0, + "Enforce W^X: 0 = no, 1 = log only, 2 = deny mapping, 3 = kill program"); + +/* + * handle_wxorx_exception: + * + * Handle W^X errors according to the sysctl setting. + * + * If the calling function should block the mapping, return true. + * Otherwise, return false. + */ +static bool +handle_wxorx_exception(void) +{ + const char *action = NULL; + bool rv = false; + + switch (enforce_wxorx) { + default: + case 3: + if ((curproc->p_flag & P_KPROC) == 0 && + (curthread->td_pflags & TDP_KTHREAD) == 0) { + kern_psignal(curproc, SIGSEGV); + action = "process killed"; + } + /* Fall Through */ + + case 2: + rv = true; + if (action == NULL) + action = "blocked"; + /* Fall Through */ + + case 1: + log(LOG_WARNING, "Detected WX mapping from pid %u (%s): %s\n", + curproc->p_pid, curproc->p_comm, + action != NULL ? action : "allowed"); + break; + + case 0: + break; + + } + return (rv); +} + /* * vm_map_insert: * @@ -1641,7 +1691,8 @@ return (KERN_INVALID_ADDRESS); if ((map->flags & MAP_WXORX) != 0 && (prot & (VM_PROT_WRITE | - VM_PROT_EXECUTE)) == (VM_PROT_WRITE | VM_PROT_EXECUTE)) + VM_PROT_EXECUTE)) == (VM_PROT_WRITE | VM_PROT_EXECUTE) && + handle_wxorx_exception()) return (KERN_PROTECTION_FAILURE); /* @@ -2728,7 +2779,7 @@ if ((map->flags & MAP_WXORX) != 0 && (flags & VM_MAP_PROTECT_SET_PROT) != 0 && (new_prot & (VM_PROT_WRITE | VM_PROT_EXECUTE)) == (VM_PROT_WRITE | - VM_PROT_EXECUTE)) { + VM_PROT_EXECUTE) && handle_wxorx_exception()) { vm_map_unlock(map); return (KERN_PROTECTION_FAILURE); }