Index: sys/compat/freebsd32/freebsd32_misc.c =================================================================== --- sys/compat/freebsd32/freebsd32_misc.c +++ sys/compat/freebsd32/freebsd32_misc.c @@ -3338,6 +3338,7 @@ case PROC_ASLR_CTL: case PROC_PROTMAX_CTL: case PROC_SPROTECT: + case PROC_STACKGAP_CTL: case PROC_TRACE_CTL: case PROC_TRAPCAP_CTL: error = copyin(PTRIN(uap->data), &flags, sizeof(flags)); @@ -3370,6 +3371,7 @@ break; case PROC_ASLR_STATUS: case PROC_PROTMAX_STATUS: + case PROC_STACKGAP_STATUS: case PROC_TRACE_STATUS: case PROC_TRAPCAP_STATUS: data = &flags; @@ -3400,6 +3402,7 @@ break; case PROC_ASLR_STATUS: case PROC_PROTMAX_STATUS: + case PROC_STACKGAP_STATUS: case PROC_TRACE_STATUS: case PROC_TRAPCAP_STATUS: if (error == 0) Index: sys/kern/kern_procctl.c =================================================================== --- sys/kern/kern_procctl.c +++ sys/kern/kern_procctl.c @@ -520,6 +520,34 @@ return (0); } +static int +stackgap_ctl(struct thread *td, struct proc *p, int state) +{ + PROC_LOCK_ASSERT(p, MA_OWNED); + + switch (state) { + case PROC_STACKGAP_ENABLE: + p->p_flag2 &= ~P2_STKGAP_DISABLE; + break; + case PROC_STACKGAP_DISABLE: + p->p_flag2 |= P2_STKGAP_DISABLE; + break; + default: + return (EINVAL); + } + return (0); +} + +static int +stackgap_status(struct thread *td, struct proc *p, int *data) +{ + PROC_LOCK_ASSERT(p, MA_OWNED); + + *data = (p->p_flag2 & P2_STKGAP_DISABLE) != 0 ? PROC_STACKGAP_DISABLE : + PROC_STACKGAP_ENABLE; + return (0); +} + #ifndef _SYS_SYSPROTO_H_ struct procctl_args { idtype_t idtype; @@ -548,6 +576,7 @@ case PROC_ASLR_CTL: case PROC_PROTMAX_CTL: case PROC_SPROTECT: + case PROC_STACKGAP_CTL: case PROC_TRACE_CTL: case PROC_TRAPCAP_CTL: error = copyin(uap->data, &flags, sizeof(flags)); @@ -578,6 +607,7 @@ break; case PROC_ASLR_STATUS: case PROC_PROTMAX_STATUS: + case PROC_STACKGAP_STATUS: case PROC_TRACE_STATUS: case PROC_TRAPCAP_STATUS: data = &flags; @@ -607,6 +637,7 @@ break; case PROC_ASLR_STATUS: case PROC_PROTMAX_STATUS: + case PROC_STACKGAP_STATUS: case PROC_TRACE_STATUS: case PROC_TRAPCAP_STATUS: if (error == 0) @@ -636,6 +667,10 @@ return (protmax_ctl(td, p, *(int *)data)); case PROC_PROTMAX_STATUS: return (protmax_status(td, p, data)); + case PROC_STACKGAP_CTL: + return (stackgap_ctl(td, p, *(int *)data)); + case PROC_STACKGAP_STATUS: + return (stackgap_status(td, p, data)); case PROC_REAP_ACQUIRE: return (reap_acquire(td, p)); case PROC_REAP_RELEASE: @@ -678,6 +713,8 @@ case PROC_REAP_STATUS: case PROC_REAP_GETPIDS: case PROC_REAP_KILL: + case PROC_STACKGAP_CTL: + case PROC_STACKGAP_STATUS: case PROC_TRACE_STATUS: case PROC_TRAPCAP_STATUS: case PROC_PDEATHSIG_CTL: @@ -726,6 +763,8 @@ case PROC_ASLR_STATUS: case PROC_PROTMAX_CTL: case PROC_PROTMAX_STATUS: + case PROC_STACKGAP_CTL: + case PROC_STACKGAP_STATUS: case PROC_TRACE_STATUS: case PROC_TRAPCAP_STATUS: tree_locked = false; Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -762,6 +762,7 @@ #define P2_ASLR_IGNSTART 0x00000100 /* Enable ASLR to consume sbrk area. */ #define P2_PROTMAX_ENABLE 0x00000200 /* Force enable implied PROT_MAX. */ #define P2_PROTMAX_DISABLE 0x00000400 /* Force disable implied PROT_MAX. */ +#define P2_STKGAP_DISABLE 0x00000800 /* Disable stack gap for MAP_STACK */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ Index: sys/sys/procctl.h =================================================================== --- sys/sys/procctl.h +++ sys/sys/procctl.h @@ -61,6 +61,8 @@ #define PROC_ASLR_STATUS 14 /* query ASLR status */ #define PROC_PROTMAX_CTL 15 /* en/dis implicit PROT_MAX */ #define PROC_PROTMAX_STATUS 16 /* query implicit PROT_MAX status */ +#define PROC_STACKGAP_CTL 17 /* en/dis stack gap on MAP_STACK */ +#define PROC_STACKGAP_STATUS 18 /* query stack gap */ /* Operations for PROC_SPROTECT (passed in integer arg). */ #define PPROT_OP(x) ((x) & 0xf) @@ -134,6 +136,9 @@ #define PROC_PROTMAX_NOFORCE 3 #define PROC_PROTMAX_ACTIVE 0x80000000 +#define PROC_STACKGAP_ENABLE 1 +#define PROC_STACKGAP_DISABLE 2 + #ifndef _KERNEL __BEGIN_DECLS int procctl(idtype_t, id_t, int, void *); Index: sys/vm/vm_map.c =================================================================== --- sys/vm/vm_map.c +++ sys/vm/vm_map.c @@ -4132,7 +4132,8 @@ addrbos + max_ssize > vm_map_max(map) || addrbos + max_ssize <= addrbos) return (KERN_INVALID_ADDRESS); - sgp = (vm_size_t)stack_guard_page * PAGE_SIZE; + sgp = (curproc->p_flag2 & P2_STKGAP_DISABLE) != 0 ? 0 : + (vm_size_t)stack_guard_page * PAGE_SIZE; if (sgp >= max_ssize) return (KERN_INVALID_ARGUMENT); @@ -4183,22 +4184,25 @@ KASSERT((orient & MAP_STACK_GROWS_UP) == 0 || (new_entry->eflags & MAP_ENTRY_GROWS_UP) != 0, ("new entry lacks MAP_ENTRY_GROWS_UP")); - rv = vm_map_insert(map, NULL, 0, gap_bot, gap_top, VM_PROT_NONE, - VM_PROT_NONE, MAP_CREATE_GUARD | (orient == MAP_STACK_GROWS_DOWN ? - MAP_CREATE_STACK_GAP_DN : MAP_CREATE_STACK_GAP_UP)); - if (rv == KERN_SUCCESS) { - /* - * Gap can never successfully handle a fault, so - * read-ahead logic is never used for it. Re-use - * next_read of the gap entry to store - * stack_guard_page for vm_map_growstack(). - */ - if (orient == MAP_STACK_GROWS_DOWN) - new_entry->prev->next_read = sgp; - else - new_entry->next->next_read = sgp; - } else { - (void)vm_map_delete(map, bot, top); + if (gap_bot != gap_top) { + rv = vm_map_insert(map, NULL, 0, gap_bot, gap_top, + VM_PROT_NONE, VM_PROT_NONE, MAP_CREATE_GUARD | + (orient == MAP_STACK_GROWS_DOWN ? + MAP_CREATE_STACK_GAP_DN : MAP_CREATE_STACK_GAP_UP)); + if (rv == KERN_SUCCESS) { + /* + * Gap can never successfully handle a fault, so + * read-ahead logic is never used for it. Re-use + * next_read of the gap entry to store + * stack_guard_page for vm_map_growstack(). + */ + if (orient == MAP_STACK_GROWS_DOWN) + new_entry->prev->next_read = sgp; + else + new_entry->next->next_read = sgp; + } else { + (void)vm_map_delete(map, bot, top); + } } return (rv); } @@ -4266,7 +4270,8 @@ } else { return (KERN_FAILURE); } - guard = gap_entry->next_read; + guard = (curproc->p_flag2 & P2_STKGAP_DISABLE) != 0 ? 0 : + gap_entry->next_read; max_grow = gap_entry->end - gap_entry->start; if (guard > max_grow) return (KERN_NO_SPACE); Index: usr.bin/proccontrol/proccontrol.c =================================================================== --- usr.bin/proccontrol/proccontrol.c +++ usr.bin/proccontrol/proccontrol.c @@ -44,6 +44,7 @@ MODE_TRACE, MODE_TRAPCAP, MODE_PROTMAX, + MODE_STACKGAP, #ifdef PROC_KPTI_CTL MODE_KPTI, #endif @@ -73,8 +74,8 @@ usage(void) { - fprintf(stderr, "Usage: proccontrol -m (aslr|protmax|trace|trapcap" - KPTI_USAGE") [-q] " + fprintf(stderr, "Usage: proccontrol -m (aslr|protmax|trace|trapcap|" + "stackgap"KPTI_USAGE") [-q] " "[-s (enable|disable)] [-p pid | command]\n"); exit(1); } @@ -101,6 +102,8 @@ mode = MODE_TRACE; else if (strcmp(optarg, "trapcap") == 0) mode = MODE_TRAPCAP; + else if (strcmp(optarg, "stackgap") == 0) + mode = MODE_STACKGAP; #ifdef PROC_KPTI_CTL else if (strcmp(optarg, "kpti") == 0) mode = MODE_KPTI; @@ -153,6 +156,9 @@ case MODE_PROTMAX: error = procctl(P_PID, pid, PROC_PROTMAX_STATUS, &arg); break; + case MODE_STACKGAP: + error = procctl(P_PID, pid, PROC_STACKGAP_STATUS, &arg); + break; #ifdef PROC_KPTI_CTL case MODE_KPTI: error = procctl(P_PID, pid, PROC_KPTI_STATUS, &arg); @@ -217,6 +223,16 @@ else printf(", not active\n"); break; + case MODE_STACKGAP: + switch (arg) { + case PROC_STACKGAP_ENABLE: + printf("enabled\n"); + break; + case PROC_STACKGAP_DISABLE: + printf("disabled\n"); + break; + } + break; #ifdef PROC_KPTI_CTL case MODE_KPTI: switch (arg & ~PROC_KPTI_STATUS_ACTIVE) { @@ -256,6 +272,11 @@ PROC_PROTMAX_FORCE_DISABLE; error = procctl(P_PID, pid, PROC_PROTMAX_CTL, &arg); break; + case MODE_STACKGAP: + arg = enable ? PROC_STACKGAP_ENABLE : + PROC_STACKGAP_DISABLE; + error = procctl(P_PID, pid, PROC_STACKGAP_CTL, &arg); + break; #ifdef PROC_KPTI_CTL case MODE_KPTI: arg = enable ? PROC_KPTI_CTL_ENABLE_ON_EXEC :