1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-11-27 08:00:11 +00:00

Add procctl(PROC_STACKGAP_CTL)

It allows a process to request that stack gap was not applied to its
stacks, retroactively.  Also it is possible to control the gaps in the
process after exec.

PR:	239894
Reviewed by:	alc
Sponsored by:	The FreeBSD Foundation
Differential revision:	https://reviews.freebsd.org/D21352
This commit is contained in:
Konstantin Belousov 2019-09-03 18:56:25 +00:00
parent 14113f123e
commit fe69291ff4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=351773
8 changed files with 144 additions and 4 deletions

View File

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 9, 2019
.Dd August 31, 2019
.Dt PROCCTL 2
.Os
.Sh NAME
@ -503,6 +503,67 @@ must point to a memory location that can hold a value of type
.Vt int .
If signal delivery has not been requested, it will contain zero
on return.
.It Dv PROC_STACKGAP_CTL
Controls the stack gaps in the specified process.
A stack gap is the part of the growth area for a
.Dv MAP_STACK
mapped region that is reserved and never filled by memory.
Instead, the process is guaranteed to receive a
.Dv SIGSEGV
signal on accessing pages in the gap.
Gaps protect against stack overflow corrupting memory adjacent
to the stack.
.Pp
The
.Fa data
argument must point to an integer variable containing flags.
The following flags are allowed:
.Bl -tag -width PROC_STACKGAP_DISABLE_EXEC
.It Dv PROC_STACKGAP_ENABLE
This flag is only accepted for consistency with
.Dv PROC_STACKGAP_STATUS .
If stack gaps are enabled, the flag is ignored.
If disabled, the flag causes an
.Ev EINVAL
error to be returned.
After gaps are disabled in a process, they can only be re-enabled when an
.Xr execve 2
is performed.
.It Dv PROC_STACKGAP_DISABLE
Disable stack gaps for the process.
For existing stacks, the gap is no longer a reserved part of the growth
area and can be filled by memory on access.
.It Dv PROC_STACKGAP_ENABLE_EXEC
Enable stack gaps for programs started after an
.Xr execve 2
by the specified process.
.It Dv PROC_STACKGAP_DISABLE_EXEC
Inherit disabled stack gaps state after
.Xr execve 2 .
In other words, if the currently executing program has stack gaps disabled,
they are kept disabled on exec.
If gaps were enabled, they are kept enabled after exec.
.El
.Pp
The stack gap state is inherited from the parent on
.Xr fork 2 .
.It Dv PROC_STACKGAP_STATUS
Returns the current stack gap state for the specified process.
.Fa data
must point to an integer variable, which is used to return a bitmask
consisting of the following flags:
.Bl -tag -width PROC_STACKGAP_DISABLE_EXEC
.It Dv PROC_STACKGAP_ENABLE
Stack gaps are enabled.
.It Dv PROC_STACKGAP_DISABLE
Stack gaps are disabled.
.It Dv PROC_STACKGAP_ENABLE_EXEC
Stack gaps are enabled in the process after
.Xr execve 2 .
.It Dv PROC_STACKGAP_DISABLE_EXEC
Stack gaps are disabled in the process after
.Xr execve 2 .
.El
.El
.Sh NOTES
Disabling tracing on a process should not be considered a security

View File

@ -3364,6 +3364,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
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));
@ -3396,6 +3397,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
break;
case PROC_ASLR_STATUS:
case PROC_PROTMAX_STATUS:
case PROC_STACKGAP_STATUS:
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
data = &flags;
@ -3426,6 +3428,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
break;
case PROC_ASLR_STATUS:
case PROC_PROTMAX_STATUS:
case PROC_STACKGAP_STATUS:
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
if (error == 0)

View File

@ -745,6 +745,8 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p)
p->p_flag |= P_EXEC;
if ((p->p_flag2 & P2_NOTRACE_EXEC) == 0)
p->p_flag2 &= ~P2_NOTRACE;
if ((p->p_flag2 & P2_STKGAP_DISABLE_EXEC) == 0)
p->p_flag2 &= ~P2_STKGAP_DISABLE;
if (p->p_flag & P_PPWAIT) {
p->p_flag &= ~(P_PPWAIT | P_PPTRACE);
cv_broadcast(&p->p_pwait);

View File

@ -460,7 +460,8 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
p2->p_flag = P_INMEM;
p2->p_flag2 = p1->p_flag2 & (P2_ASLR_DISABLE | P2_ASLR_ENABLE |
P2_ASLR_IGNSTART | P2_NOTRACE | P2_NOTRACE_EXEC |
P2_PROTMAX_ENABLE | P2_PROTMAX_DISABLE | P2_TRAPCAP);
P2_PROTMAX_ENABLE | P2_PROTMAX_DISABLE | P2_TRAPCAP |
P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC);
p2->p_swtick = ticks;
if (p1->p_flag & P_PROFIL)
startprofclock(p2);

View File

@ -520,6 +520,55 @@ aslr_status(struct thread *td, struct proc *p, int *data)
return (0);
}
static int
stackgap_ctl(struct thread *td, struct proc *p, int state)
{
PROC_LOCK_ASSERT(p, MA_OWNED);
if ((state & ~(PROC_STACKGAP_ENABLE | PROC_STACKGAP_DISABLE |
PROC_STACKGAP_ENABLE_EXEC | PROC_STACKGAP_DISABLE_EXEC)) != 0)
return (EINVAL);
switch (state & (PROC_STACKGAP_ENABLE | PROC_STACKGAP_DISABLE)) {
case PROC_STACKGAP_ENABLE:
if ((p->p_flag2 & P2_STKGAP_DISABLE) != 0)
return (EINVAL);
break;
case PROC_STACKGAP_DISABLE:
p->p_flag2 |= P2_STKGAP_DISABLE;
break;
case 0:
break;
default:
return (EINVAL);
}
switch (state & (PROC_STACKGAP_ENABLE_EXEC |
PROC_STACKGAP_DISABLE_EXEC)) {
case PROC_STACKGAP_ENABLE_EXEC:
p->p_flag2 &= ~P2_STKGAP_DISABLE_EXEC;
break;
case PROC_STACKGAP_DISABLE_EXEC:
p->p_flag2 |= P2_STKGAP_DISABLE_EXEC;
break;
case 0:
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;
*data |= (p->p_flag2 & P2_STKGAP_DISABLE_EXEC) != 0 ?
PROC_STACKGAP_DISABLE_EXEC : PROC_STACKGAP_ENABLE_EXEC;
return (0);
}
#ifndef _SYS_SYSPROTO_H_
struct procctl_args {
idtype_t idtype;
@ -548,6 +597,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
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 +628,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
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 +658,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
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 +688,10 @@ kern_procctl_single(struct thread *td, struct proc *p, int com, void *data)
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 +734,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
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 +784,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
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;

View File

@ -762,6 +762,8 @@ struct proc {
#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 */
#define P2_STKGAP_DISABLE_EXEC 0x00001000 /* Stack gap disabled after exec */
/* Flags protected by proctree_lock, kept in p_treeflags. */
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */

View File

@ -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,11 @@ struct procctl_reaper_kill {
#define PROC_PROTMAX_NOFORCE 3
#define PROC_PROTMAX_ACTIVE 0x80000000
#define PROC_STACKGAP_ENABLE 0x0001
#define PROC_STACKGAP_DISABLE 0x0002
#define PROC_STACKGAP_ENABLE_EXEC 0x0004
#define PROC_STACKGAP_DISABLE_EXEC 0x0008
#ifndef _KERNEL
__BEGIN_DECLS
int procctl(idtype_t, id_t, int, void *);

View File

@ -4132,7 +4132,8 @@ vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize,
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,6 +4184,8 @@ vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize,
KASSERT((orient & MAP_STACK_GROWS_UP) == 0 ||
(new_entry->eflags & MAP_ENTRY_GROWS_UP) != 0,
("new entry lacks MAP_ENTRY_GROWS_UP"));
if (gap_bot == gap_top)
return (KERN_SUCCESS);
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));
@ -4266,7 +4269,8 @@ vm_map_growstack(vm_map_t map, vm_offset_t addr, vm_map_entry_t gap_entry)
} 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);