Changeset View
Standalone View
sys/kern/kern_procctl.c
Show All 37 Lines | |||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/procctl.h> | #include <sys/procctl.h> | ||||
#include <sys/sx.h> | #include <sys/sx.h> | ||||
#include <sys/syscallsubr.h> | #include <sys/syscallsubr.h> | ||||
#include <sys/sysproto.h> | #include <sys/sysproto.h> | ||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <vm/vm.h> | |||||
#include <vm/pmap.h> | |||||
#include <vm/vm_map.h> | |||||
#include <vm/vm_extern.h> | |||||
static int | static int | ||||
protect_setchild(struct thread *td, struct proc *p, int flags) | protect_setchild(struct thread *td, struct proc *p, int flags) | ||||
{ | { | ||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | ||||
if (p->p_flag & P_SYSTEM || p_cansched(td, p) != 0) | if (p->p_flag & P_SYSTEM || p_cansched(td, p) != 0) | ||||
return (0); | return (0); | ||||
if (flags & PPROT_SET) { | if (flags & PPROT_SET) { | ||||
▲ Show 20 Lines • Show All 305 Lines • ▼ Show 20 Lines | |||||
trapcap_status(struct thread *td, struct proc *p, int *data) | trapcap_status(struct thread *td, struct proc *p, int *data) | ||||
{ | { | ||||
*data = (p->p_flag2 & P2_TRAPCAP) != 0 ? PROC_TRAPCAP_CTL_ENABLE : | *data = (p->p_flag2 & P2_TRAPCAP) != 0 ? PROC_TRAPCAP_CTL_ENABLE : | ||||
PROC_TRAPCAP_CTL_DISABLE; | PROC_TRAPCAP_CTL_DISABLE; | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
aslr_ctl(struct thread *td, struct proc *p, int state) | |||||
{ | |||||
PROC_LOCK_ASSERT(p, MA_OWNED); | |||||
switch (state) { | |||||
case PROC_ASLR_FORCE_ENABLE: | |||||
p->p_flag2 &= ~P2_ASLR_DISABLE; | |||||
p->p_flag2 |= P2_ASLR_ENABLE; | |||||
break; | |||||
case PROC_ASLR_FORCE_DISABLE: | |||||
p->p_flag2 |= P2_ASLR_DISABLE; | |||||
p->p_flag2 &= ~P2_ASLR_ENABLE; | |||||
break; | |||||
case PROC_ASLR_NOFORCE: | |||||
p->p_flag2 &= ~(P2_ASLR_ENABLE | P2_ASLR_DISABLE); | |||||
break; | |||||
default: | |||||
return (EINVAL); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
aslr_status(struct thread *td, struct proc *p, int *data) | |||||
{ | |||||
struct vmspace *vm; | |||||
int d; | |||||
switch (p->p_flag2 & (P2_ASLR_ENABLE | P2_ASLR_DISABLE)) { | |||||
case 0: | |||||
d = PROC_ASLR_NOFORCE; | |||||
break; | |||||
case P2_ASLR_ENABLE: | |||||
d = PROC_ASLR_FORCE_ENABLE; | |||||
break; | |||||
case P2_ASLR_DISABLE: | |||||
d = PROC_ASLR_FORCE_DISABLE; | |||||
break; | |||||
} | |||||
if ((p->p_flag & P_WEXIT) == 0) { | |||||
_PHOLD(p); | |||||
PROC_UNLOCK(p); | |||||
vm = vmspace_acquire_ref(p); | |||||
if (vm != NULL && (vm->vm_map.flags & MAP_ASLR) != 0) { | |||||
d |= PROC_ASLR_ACTIVE; | |||||
vmspace_free(vm); | |||||
} | |||||
PROC_LOCK(p); | |||||
_PRELE(p); | |||||
} | |||||
*data = d; | |||||
return (0); | |||||
} | |||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct procctl_args { | struct procctl_args { | ||||
idtype_t idtype; | idtype_t idtype; | ||||
id_t id; | id_t id; | ||||
int com; | int com; | ||||
void *data; | void *data; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_procctl(struct thread *td, struct procctl_args *uap) | sys_procctl(struct thread *td, struct procctl_args *uap) | ||||
{ | { | ||||
void *data; | void *data; | ||||
union { | union { | ||||
struct procctl_reaper_status rs; | struct procctl_reaper_status rs; | ||||
struct procctl_reaper_pids rp; | struct procctl_reaper_pids rp; | ||||
struct procctl_reaper_kill rk; | struct procctl_reaper_kill rk; | ||||
} x; | } x; | ||||
int error, error1, flags; | int error, error1, flags; | ||||
switch (uap->com) { | switch (uap->com) { | ||||
case PROC_SPROTECT: | case PROC_SPROTECT: | ||||
case PROC_TRACE_CTL: | case PROC_TRACE_CTL: | ||||
case PROC_TRAPCAP_CTL: | case PROC_TRAPCAP_CTL: | ||||
lattera-gmail.com: FreeBSD users should know that an attacker can abuse this API to disable ASLR. | |||||
Not Done Inline ActionsThis also raises the point that PROC_ASLR_CTL needs to be documented in procctl(2) emaste: This also raises the point that PROC_ASLR_CTL needs to be documented in procctl(2) | |||||
Not Done Inline ActionsPerhaps it should be sysctl+securelevel protected. bdrewery: Perhaps it should be sysctl+securelevel protected. | |||||
Not Done Inline ActionsWhy ? Did you read the code ? It only affects the policy for the new address space after exec. That said, to successfully call procctl(2), you must have debugging privilege on the target process. So you can either debug the target, or you are doing it against yourself (which means that malicious code is already executing in this address space). Why do you need to disable something similar to ASLR at all, then ? https://blogs.msdn.microsoft.com/oldnewthing/20071031-00/?p=24633/ kib: Why ? Did you read the code ? It only affects the policy for the new address space after exec. | |||||
case PROC_ASLR_CTL: | |||||
error = copyin(uap->data, &flags, sizeof(flags)); | error = copyin(uap->data, &flags, sizeof(flags)); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
data = &flags; | data = &flags; | ||||
break; | break; | ||||
case PROC_REAP_ACQUIRE: | case PROC_REAP_ACQUIRE: | ||||
case PROC_REAP_RELEASE: | case PROC_REAP_RELEASE: | ||||
if (uap->data != NULL) | if (uap->data != NULL) | ||||
Show All 12 Lines | sys_procctl(struct thread *td, struct procctl_args *uap) | ||||
case PROC_REAP_KILL: | case PROC_REAP_KILL: | ||||
error = copyin(uap->data, &x.rk, sizeof(x.rk)); | error = copyin(uap->data, &x.rk, sizeof(x.rk)); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
data = &x.rk; | data = &x.rk; | ||||
break; | break; | ||||
case PROC_TRACE_STATUS: | case PROC_TRACE_STATUS: | ||||
case PROC_TRAPCAP_STATUS: | case PROC_TRAPCAP_STATUS: | ||||
case PROC_ASLR_STATUS: | |||||
data = &flags; | data = &flags; | ||||
break; | break; | ||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
error = kern_procctl(td, uap->idtype, uap->id, uap->com, data); | error = kern_procctl(td, uap->idtype, uap->id, uap->com, data); | ||||
switch (uap->com) { | switch (uap->com) { | ||||
case PROC_REAP_STATUS: | case PROC_REAP_STATUS: | ||||
if (error == 0) | if (error == 0) | ||||
error = copyout(&x.rs, uap->data, sizeof(x.rs)); | error = copyout(&x.rs, uap->data, sizeof(x.rs)); | ||||
break; | break; | ||||
case PROC_REAP_KILL: | case PROC_REAP_KILL: | ||||
error1 = copyout(&x.rk, uap->data, sizeof(x.rk)); | error1 = copyout(&x.rk, uap->data, sizeof(x.rk)); | ||||
if (error == 0) | if (error == 0) | ||||
error = error1; | error = error1; | ||||
break; | break; | ||||
case PROC_TRACE_STATUS: | case PROC_TRACE_STATUS: | ||||
case PROC_TRAPCAP_STATUS: | case PROC_TRAPCAP_STATUS: | ||||
case PROC_ASLR_STATUS: | |||||
if (error == 0) | if (error == 0) | ||||
error = copyout(&flags, uap->data, sizeof(flags)); | error = copyout(&flags, uap->data, sizeof(flags)); | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
Show All 17 Lines | kern_procctl_single(struct thread *td, struct proc *p, int com, void *data) | ||||
case PROC_TRACE_CTL: | case PROC_TRACE_CTL: | ||||
return (trace_ctl(td, p, *(int *)data)); | return (trace_ctl(td, p, *(int *)data)); | ||||
case PROC_TRACE_STATUS: | case PROC_TRACE_STATUS: | ||||
return (trace_status(td, p, data)); | return (trace_status(td, p, data)); | ||||
case PROC_TRAPCAP_CTL: | case PROC_TRAPCAP_CTL: | ||||
return (trapcap_ctl(td, p, *(int *)data)); | return (trapcap_ctl(td, p, *(int *)data)); | ||||
case PROC_TRAPCAP_STATUS: | case PROC_TRAPCAP_STATUS: | ||||
return (trapcap_status(td, p, data)); | return (trapcap_status(td, p, data)); | ||||
case PROC_ASLR_CTL: | |||||
return (aslr_ctl(td, p, *(int *)data)); | |||||
case PROC_ASLR_STATUS: | |||||
return (aslr_status(td, p, data)); | |||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
} | } | ||||
int | int | ||||
kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data) | kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data) | ||||
{ | { | ||||
struct pgrp *pg; | struct pgrp *pg; | ||||
struct proc *p; | struct proc *p; | ||||
int error, first_error, ok; | int error, first_error, ok; | ||||
bool tree_locked; | bool tree_locked; | ||||
switch (com) { | switch (com) { | ||||
case PROC_REAP_ACQUIRE: | case PROC_REAP_ACQUIRE: | ||||
case PROC_REAP_RELEASE: | case PROC_REAP_RELEASE: | ||||
case PROC_REAP_STATUS: | case PROC_REAP_STATUS: | ||||
case PROC_REAP_GETPIDS: | case PROC_REAP_GETPIDS: | ||||
case PROC_REAP_KILL: | case PROC_REAP_KILL: | ||||
case PROC_TRACE_STATUS: | case PROC_TRACE_STATUS: | ||||
case PROC_TRAPCAP_STATUS: | case PROC_TRAPCAP_STATUS: | ||||
case PROC_ASLR_CTL: | |||||
case PROC_ASLR_STATUS: | |||||
Not Done Inline ActionsAbsent a need for a specific order I think we should prefer alphabetical ordering for lists like this. I had sorted them in my local tree, and have put that change in D8304. emaste: Absent a need for a specific order I think we should prefer alphabetical ordering for lists… | |||||
if (idtype != P_PID) | if (idtype != P_PID) | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
switch (com) { | switch (com) { | ||||
case PROC_SPROTECT: | case PROC_SPROTECT: | ||||
case PROC_REAP_STATUS: | case PROC_REAP_STATUS: | ||||
case PROC_REAP_GETPIDS: | case PROC_REAP_GETPIDS: | ||||
case PROC_REAP_KILL: | case PROC_REAP_KILL: | ||||
case PROC_TRACE_CTL: | case PROC_TRACE_CTL: | ||||
case PROC_TRAPCAP_CTL: | case PROC_TRAPCAP_CTL: | ||||
sx_slock(&proctree_lock); | sx_slock(&proctree_lock); | ||||
tree_locked = true; | tree_locked = true; | ||||
break; | break; | ||||
case PROC_REAP_ACQUIRE: | case PROC_REAP_ACQUIRE: | ||||
case PROC_REAP_RELEASE: | case PROC_REAP_RELEASE: | ||||
sx_xlock(&proctree_lock); | sx_xlock(&proctree_lock); | ||||
tree_locked = true; | tree_locked = true; | ||||
break; | break; | ||||
case PROC_TRACE_STATUS: | case PROC_TRACE_STATUS: | ||||
case PROC_TRAPCAP_STATUS: | case PROC_TRAPCAP_STATUS: | ||||
case PROC_ASLR_CTL: | |||||
case PROC_ASLR_STATUS: | |||||
tree_locked = false; | tree_locked = false; | ||||
break; | break; | ||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
switch (idtype) { | switch (idtype) { | ||||
case P_PID: | case P_PID: | ||||
▲ Show 20 Lines • Show All 57 Lines • Show Last 20 Lines |
FreeBSD users should know that an attacker can abuse this API to disable ASLR.