mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 04:43:53 +00:00
procctl(2): Add PROC_WXMAP_CTL/STATUS
It allows to override kern.elf{32,64}.allow_wx on per-process basis. In particular, it makes it possible to run binaries without PT_GNU_STACK and without elfctl note while allow_wx = 0. Reviewed by: brooks, emaste, markj Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D31779
This commit is contained in:
parent
1349891a0e
commit
796a8e1ad1
|
@ -29,7 +29,7 @@
|
|||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd July 1, 2021
|
||||
.Dd September 2, 2021
|
||||
.Dt PROCCTL 2
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -599,6 +599,62 @@ following values is written:
|
|||
.It Dv PROC_NO_NEW_PRIVS_ENABLE
|
||||
.It Dv PROC_NO_NEW_PRIVS_DISABLE
|
||||
.El
|
||||
.It Dv PROC_WXMAP_CTL
|
||||
Controls the 'write exclusive against execution' permissions for the
|
||||
mappings in the process address space.
|
||||
It overrides the global settings established by the
|
||||
.Dv kern.elf{32/64}.allow_wx
|
||||
sysctl,
|
||||
and the corresponding bit in the ELF control note, see
|
||||
.Xr elfctl 1 .
|
||||
.Pp
|
||||
The
|
||||
.Fa data
|
||||
parameter must point to the integer variable holding one of the
|
||||
following values:
|
||||
.Bl -tag -width PROC_WX_MAPPINGS_DISALLOW_EXEC
|
||||
.It Dv PROC_WX_MAPPINGS_PERMIT
|
||||
Enable creation of mappings that have both write and execute
|
||||
protection attributes, in the specified process' address space.
|
||||
.It Dv PROC_WX_MAPPINGS_DISALLOW_EXEC
|
||||
In the new address space created by
|
||||
.Xr execve 2 ,
|
||||
disallow creation of mappings that have both write and execute
|
||||
permissions.
|
||||
.El
|
||||
.Pp
|
||||
Once creation of writeable and executable mappings is allowed,
|
||||
it is impossible (and pointless) to disallow it.
|
||||
The only way to ensure the absence of such mappings after they
|
||||
were enabled in a given process, is to set the
|
||||
.Dv PROC_WX_MAPPINGS_DISALLOW_EXEC
|
||||
flag and
|
||||
.Xr execve 2
|
||||
an image.
|
||||
.It Dv PROC_WXMAP_STATUS
|
||||
Returns the current status of the 'write exclusive against execution'
|
||||
enforcement for the specified process.
|
||||
The
|
||||
.Dv data
|
||||
parameter must point to the integer variable, where one of the
|
||||
following values is written:
|
||||
.Bl -tag -width PROC_WX_MAPPINGS_DISALLOW_EXEC
|
||||
.It Dv PROC_WX_MAPPINGS_PERMIT
|
||||
Creation of simultaneously writable and executable mapping is permitted,
|
||||
otherwise the process cannot create such mappings.
|
||||
.It Dv PROC_WX_MAPPINGS_DISALLOW_EXEC
|
||||
After
|
||||
.Xr execve 2 ,
|
||||
the new address space should disallow creation of simultaneously
|
||||
writable and executable mappings.
|
||||
.El
|
||||
.Pp
|
||||
Additionally, if the address space of the process disallows
|
||||
creation of simultaneously writable and executable mappings and
|
||||
it is guaranteed that no such mapping was created since address space
|
||||
creation, the
|
||||
.Dv PROC_WXORX_ENFORCE
|
||||
flag is set in the returned value.
|
||||
.El
|
||||
.Sh x86 MACHINE-SPECIFIC REQUESTS
|
||||
.Bl -tag -width PROC_KPTI_STATUS
|
||||
|
@ -648,6 +704,12 @@ feature, as it is bypassable both by the kernel and privileged processes,
|
|||
and via other system mechanisms.
|
||||
As such, it should not be utilized to reliably protect cryptographic
|
||||
keying material or other confidential data.
|
||||
.Pp
|
||||
Note that processes can trivially bypass the 'no simultaneously
|
||||
writable and executable mappings' policy by first marking some mapping
|
||||
as writeable and write code to it, then removing write and adding
|
||||
execute permission.
|
||||
This may be legitimately required by some programs, such as JIT compilers.
|
||||
.Sh RETURN VALUES
|
||||
If an error occurs, a value of -1 is returned and
|
||||
.Va errno
|
||||
|
|
|
@ -3643,6 +3643,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
|||
case PROC_TRACE_CTL:
|
||||
case PROC_TRAPCAP_CTL:
|
||||
case PROC_NO_NEW_PRIVS_CTL:
|
||||
case PROC_WXMAP_CTL:
|
||||
error = copyin(PTRIN(uap->data), &flags, sizeof(flags));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
@ -3677,6 +3678,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
|||
case PROC_TRACE_STATUS:
|
||||
case PROC_TRAPCAP_STATUS:
|
||||
case PROC_NO_NEW_PRIVS_STATUS:
|
||||
case PROC_WXMAP_STATUS:
|
||||
data = &flags;
|
||||
break;
|
||||
case PROC_PDEATHSIG_CTL:
|
||||
|
@ -3709,6 +3711,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
|||
case PROC_TRACE_STATUS:
|
||||
case PROC_TRAPCAP_STATUS:
|
||||
case PROC_NO_NEW_PRIVS_STATUS:
|
||||
case PROC_WXMAP_STATUS:
|
||||
if (error == 0)
|
||||
error = copyout(&flags, uap->data, sizeof(flags));
|
||||
break;
|
||||
|
|
|
@ -1216,7 +1216,8 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
|
|||
*/
|
||||
if (imgp->credential_setid) {
|
||||
PROC_LOCK(imgp->proc);
|
||||
imgp->proc->p_flag2 &= ~(P2_ASLR_ENABLE | P2_ASLR_DISABLE);
|
||||
imgp->proc->p_flag2 &= ~(P2_ASLR_ENABLE | P2_ASLR_DISABLE |
|
||||
P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC);
|
||||
PROC_UNLOCK(imgp->proc);
|
||||
}
|
||||
if ((sv->sv_flags & SV_ASLR) == 0 ||
|
||||
|
@ -1239,7 +1240,9 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
|
|||
imgp->map_flags |= MAP_ASLR_IGNSTART;
|
||||
}
|
||||
|
||||
if (!__elfN(allow_wx) && (fctl0 & NT_FREEBSD_FCTL_WXNEEDED) == 0)
|
||||
if ((!__elfN(allow_wx) && (fctl0 & NT_FREEBSD_FCTL_WXNEEDED) == 0 &&
|
||||
(imgp->proc->p_flag2 & P2_WXORX_DISABLE) == 0) ||
|
||||
(imgp->proc->p_flag2 & P2_WXORX_ENABLE_EXEC) != 0)
|
||||
imgp->map_flags |= MAP_WXORX;
|
||||
|
||||
error = exec_new_vmspace(imgp, sv);
|
||||
|
|
|
@ -493,7 +493,8 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
|
|||
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_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC | P2_NO_NEW_PRIVS);
|
||||
P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC | P2_NO_NEW_PRIVS |
|
||||
P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC);
|
||||
p2->p_swtick = ticks;
|
||||
if (p1->p_flag & P_PROFIL)
|
||||
startprofclock(p2);
|
||||
|
|
|
@ -591,6 +591,71 @@ stackgap_status(struct thread *td, struct proc *p, int *data)
|
|||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
wxmap_ctl(struct thread *td, struct proc *p, int state)
|
||||
{
|
||||
struct vmspace *vm;
|
||||
vm_map_t map;
|
||||
|
||||
PROC_LOCK_ASSERT(p, MA_OWNED);
|
||||
if ((p->p_flag & P_WEXIT) != 0)
|
||||
return (ESRCH);
|
||||
|
||||
switch (state) {
|
||||
case PROC_WX_MAPPINGS_PERMIT:
|
||||
p->p_flag2 |= P2_WXORX_DISABLE;
|
||||
_PHOLD(p);
|
||||
PROC_UNLOCK(p);
|
||||
vm = vmspace_acquire_ref(p);
|
||||
if (vm != NULL) {
|
||||
map = &vm->vm_map;
|
||||
vm_map_lock(map);
|
||||
map->flags &= ~MAP_WXORX;
|
||||
vm_map_unlock(map);
|
||||
vmspace_free(vm);
|
||||
}
|
||||
PROC_LOCK(p);
|
||||
_PRELE(p);
|
||||
break;
|
||||
case PROC_WX_MAPPINGS_DISALLOW_EXEC:
|
||||
p->p_flag2 |= P2_WXORX_ENABLE_EXEC;
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
wxmap_status(struct thread *td, struct proc *p, int *data)
|
||||
{
|
||||
struct vmspace *vm;
|
||||
int d;
|
||||
|
||||
PROC_LOCK_ASSERT(p, MA_OWNED);
|
||||
if ((p->p_flag & P_WEXIT) != 0)
|
||||
return (ESRCH);
|
||||
|
||||
d = 0;
|
||||
if ((p->p_flag2 & P2_WXORX_DISABLE) != 0)
|
||||
d |= PROC_WX_MAPPINGS_PERMIT;
|
||||
if ((p->p_flag2 & P2_WXORX_ENABLE_EXEC) != 0)
|
||||
d |= PROC_WX_MAPPINGS_DISALLOW_EXEC;
|
||||
_PHOLD(p);
|
||||
PROC_UNLOCK(p);
|
||||
vm = vmspace_acquire_ref(p);
|
||||
if (vm != NULL) {
|
||||
if ((vm->vm_map.flags & MAP_WXORX) != 0)
|
||||
d |= PROC_WXORX_ENFORCE;
|
||||
vmspace_free(vm);
|
||||
}
|
||||
PROC_LOCK(p);
|
||||
_PRELE(p);
|
||||
*data = d;
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifndef _SYS_SYSPROTO_H_
|
||||
struct procctl_args {
|
||||
idtype_t idtype;
|
||||
|
@ -623,6 +688,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
|||
case PROC_TRACE_CTL:
|
||||
case PROC_TRAPCAP_CTL:
|
||||
case PROC_NO_NEW_PRIVS_CTL:
|
||||
case PROC_WXMAP_CTL:
|
||||
error = copyin(uap->data, &flags, sizeof(flags));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
@ -655,6 +721,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
|||
case PROC_TRACE_STATUS:
|
||||
case PROC_TRAPCAP_STATUS:
|
||||
case PROC_NO_NEW_PRIVS_STATUS:
|
||||
case PROC_WXMAP_STATUS:
|
||||
data = &flags;
|
||||
break;
|
||||
case PROC_PDEATHSIG_CTL:
|
||||
|
@ -686,6 +753,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
|||
case PROC_TRACE_STATUS:
|
||||
case PROC_TRAPCAP_STATUS:
|
||||
case PROC_NO_NEW_PRIVS_STATUS:
|
||||
case PROC_WXMAP_STATUS:
|
||||
if (error == 0)
|
||||
error = copyout(&flags, uap->data, sizeof(flags));
|
||||
break;
|
||||
|
@ -739,6 +807,10 @@ kern_procctl_single(struct thread *td, struct proc *p, int com, void *data)
|
|||
return (no_new_privs_ctl(td, p, *(int *)data));
|
||||
case PROC_NO_NEW_PRIVS_STATUS:
|
||||
return (no_new_privs_status(td, p, data));
|
||||
case PROC_WXMAP_CTL:
|
||||
return (wxmap_ctl(td, p, *(int *)data));
|
||||
case PROC_WXMAP_STATUS:
|
||||
return (wxmap_status(td, p, data));
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
|
@ -771,6 +843,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
|
|||
case PROC_PDEATHSIG_STATUS:
|
||||
case PROC_NO_NEW_PRIVS_CTL:
|
||||
case PROC_NO_NEW_PRIVS_STATUS:
|
||||
case PROC_WXMAP_CTL:
|
||||
case PROC_WXMAP_STATUS:
|
||||
if (idtype != P_PID)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
@ -821,6 +895,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
|
|||
case PROC_TRACE_STATUS:
|
||||
case PROC_TRAPCAP_STATUS:
|
||||
case PROC_NO_NEW_PRIVS_STATUS:
|
||||
case PROC_WXMAP_CTL:
|
||||
case PROC_WXMAP_STATUS:
|
||||
tree_locked = false;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -838,6 +838,8 @@ struct proc {
|
|||
#define P2_ITSTOPPED 0x00002000
|
||||
#define P2_PTRACEREQ 0x00004000 /* Active ptrace req */
|
||||
#define P2_NO_NEW_PRIVS 0x00008000 /* Ignore setuid */
|
||||
#define P2_WXORX_DISABLE 0x00010000 /* WX mappings enabled */
|
||||
#define P2_WXORX_ENABLE_EXEC 0x00020000 /* WXORX enabled after exec */
|
||||
|
||||
/* Flags protected by proctree_lock, kept in p_treeflags. */
|
||||
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
|
||||
|
|
|
@ -65,6 +65,8 @@
|
|||
#define PROC_STACKGAP_STATUS 18 /* query stack gap */
|
||||
#define PROC_NO_NEW_PRIVS_CTL 19 /* disable setuid/setgid */
|
||||
#define PROC_NO_NEW_PRIVS_STATUS 20 /* query suid/sgid disabled status */
|
||||
#define PROC_WXMAP_CTL 21 /* control W^X */
|
||||
#define PROC_WXMAP_STATUS 22 /* query W^X */
|
||||
|
||||
/* Operations for PROC_SPROTECT (passed in integer arg). */
|
||||
#define PPROT_OP(x) ((x) & 0xf)
|
||||
|
@ -146,6 +148,10 @@ struct procctl_reaper_kill {
|
|||
#define PROC_NO_NEW_PRIVS_ENABLE 1
|
||||
#define PROC_NO_NEW_PRIVS_DISABLE 2
|
||||
|
||||
#define PROC_WX_MAPPINGS_PERMIT 0x0001
|
||||
#define PROC_WX_MAPPINGS_DISALLOW_EXEC 0x0002
|
||||
#define PROC_WXORX_ENFORCE 0x80000000
|
||||
|
||||
#ifndef _KERNEL
|
||||
__BEGIN_DECLS
|
||||
int procctl(idtype_t, id_t, int, void *);
|
||||
|
|
Loading…
Reference in a new issue