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:
parent
14113f123e
commit
fe69291ff4
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=351773
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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 *);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user