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/man3/siginfo.3 b/share/man/man3/siginfo.3 --- a/share/man/man3/siginfo.3 +++ b/share/man/man3/siginfo.3 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 17, 2021 +.Dd September 16, 2022 .Dt SIGINFO 3 .Os .Sh NAME @@ -162,6 +162,8 @@ DTrace induced trap .It Ta Dv TRAP_CAP Ta capabilities protective trap +.It Ta Dv TRAP_WXORX Ta +W^X violation trap .It Dv SIGCHLD Ta Dv CLD_EXITED Ta child has exited .It Ta Dv CLD_KILLED Ta 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 21, 2022 .Dt SECURITY 7 .Os .Sh NAME @@ -938,6 +938,59 @@ .Pa authorized_keys file to make the key only usable to entities logging in from specific machines. +.Sh EXECUTABLE MEMORY PROTECTION +Some hardware allows you to control which memory regions are allowed to contain +executable instructions. +The hardware will block attempts to execute code from memory which is not +configured to allow executable instructions. +.Pp +One security measure you can use to block certain kinds of attacks is to +ensure that the same memory region is not writable and executable at the same +time. +This security measure (sometimes called W^X) attempts to limit an attacker's +ability to overwrite executable code. +.Fx +provides configuration controls to enable this protection for user-space code. +.Pp +There are two sets of controls. +One set of controls chooses the user-space processes for which +.Fx +should try to enforce the W^X restriction. +The second set of controls specifies how +.Fx +should respond when a user-space program violates the W^X restriction. +.Pp +The set of controls which chooses the user-space processes for which +.Fx +should try to enforce the W^X restriction includes: +.Bl -bullet +.It +The +.Va kern.elf32.allow_wx +and/or +.Va kern.elf64.allow_wx +sysctls and/or tunables. (Described below.) +.It +The +.Va wxmap +mode argument to the +.Xr proccontrol 1 +command. +.El +.Pp +The set of controls which specifies how +.Fx +should respond when a user-space program violates the W^X restriction includes: +.Bl -bullet +.It +The +.Va vm.enforce_wxorx +sysctl/tunable. (Described below.) +.It +The +.Va vm.log_wxorx +sysctl/tunable. (Described below.) +.El .Sh KNOBS AND TWEAKS .Fx provides several knobs and tweak handles that make some introspection @@ -1082,9 +1135,49 @@ .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 +Block page mappings which are simultaneously writable and executable. +.It Ic 2 +Send a +.Dv SIGTRAP +signal with code +.Dv TRAP_WXORX +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. +.It Dv vm.log_wxorx +Controls whether the system logs attempts to map memory pages which are +simultaneously writable and executable for user processes where such +enforcement has been requested. +This can be enabled separately from enforcement to allow a system to log +requests which would have been denied if enforcement was enabled. .El .Sh SEE ALSO .Xr chflags 1 , diff --git a/sys/compat/linux/linux_signal.c b/sys/compat/linux/linux_signal.c --- a/sys/compat/linux/linux_signal.c +++ b/sys/compat/linux/linux_signal.c @@ -647,6 +647,7 @@ case TRAP_DTRACE: return (LINUX_TRAP_TRACE); case TRAP_CAP: + case TRAP_WXORX: return (LINUX_TRAP_UNK); default: return (si_code); 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 @@ -219,7 +219,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 +744,13 @@ /* 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)) { + uprintf("Refusing to run ELF binary with WX memory" + " mapping when WX mappings disallowed\n"); + 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/sys/signal.h b/sys/sys/signal.h --- a/sys/sys/signal.h +++ b/sys/sys/signal.h @@ -346,6 +346,7 @@ #define TRAP_TRACE 2 /* Process trace trap. */ #define TRAP_DTRACE 3 /* DTrace induced trap. */ #define TRAP_CAP 4 /* Capabilities protective trap. */ +#define TRAP_WXORX 5 /* W^X violation trap. */ /* codes for SIGCHLD */ #define CLD_EXITED 1 /* Child has exited */ 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,63 @@ 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 = deny mapping, 2 = send trap to process"); + +static bool log_wxorx = false; +SYSCTL_BOOL(_vm, OID_AUTO, log_wxorx, CTLFLAG_RWTUN, &log_wxorx, 0, + "Log W^X violations"); + +/* + * 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) +{ + ksiginfo_t ksi; + const char *action = NULL; + struct thread *td; + bool rv; + + switch (enforce_wxorx) { + case 2: + td = curthread; + if ((td->td_proc->p_flag & P_SYSTEM) == 0 && + td->td_proc->p_state != PRS_NEW && + (td->td_pflags & TDP_KTHREAD) == 0) { + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGTRAP; + ksi.ksi_code = TRAP_WXORX; + ksi.ksi_errno = EINVAL; + trapsignal(td, &ksi); + action = "sent trap to process"; + } + /* Fall Through */ + + default: + case 1: + rv = true; + if (action == NULL) + action = "blocked"; + break; + + case 0: + rv = false; + action = "allowed"; + break; + } + if (log_wxorx) + log(LOG_WARNING, "Detected WX mapping from pid %u (%s): %s\n", + curproc->p_pid, curproc->p_comm, action); + return (rv); +} + /* * vm_map_insert: * @@ -1641,7 +1700,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 +2788,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); }