aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2026-05-26 17:36:20 +0000
committerMark Johnston <markj@FreeBSD.org>2026-06-29 19:17:29 +0000
commit8fbbc185a3ff66d95c30d9b9062d6d8a349f0454 (patch)
tree0b969b1749395458518e72fccb90a9247fd64352 /sys
parent4c9e89c85d7c6cf50b09865de4dacedd015c5f39 (diff)
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/linprocfs/linprocfs.c16
-rw-r--r--sys/compat/linux/linux_misc.c15
-rw-r--r--sys/fs/cuse/cuse.c4
-rw-r--r--sys/fs/procfs/procfs_map.c17
-rw-r--r--sys/fs/procfs/procfs_mem.c6
-rw-r--r--sys/fs/pseudofs/pseudofs_vnops.c69
-rw-r--r--sys/kern/kern_event.c22
-rw-r--r--sys/kern/kern_exec.c81
-rw-r--r--sys/kern/kern_exit.c1
-rw-r--r--sys/kern/kern_fork.c1
-rw-r--r--sys/kern/kern_proc.c191
-rw-r--r--sys/kern/kern_procctl.c23
-rw-r--r--sys/kern/kern_prot.c20
-rw-r--r--sys/kern/kern_resource.c27
-rw-r--r--sys/kern/sys_process.c124
-rw-r--r--sys/sys/imgact.h4
-rw-r--r--sys/sys/proc.h3
-rw-r--r--sys/sys/ptrace.h15
18 files changed, 467 insertions, 172 deletions
diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c
index d4f38b491471..b42cbea61d09 100644
--- a/sys/compat/linprocfs/linprocfs.c
+++ b/sys/compat/linprocfs/linprocfs.c
@@ -1317,19 +1317,13 @@ linprocfs_doprocmaps(PFS_FILL_ARGS)
struct vattr vat;
bool private;
- PROC_LOCK(p);
- error = p_candebug(td, p);
- PROC_UNLOCK(p);
- if (error)
- return (error);
-
if (uio->uio_rw != UIO_READ)
return (EOPNOTSUPP);
- error = 0;
- vm = vmspace_acquire_ref(p);
- if (vm == NULL)
- return (ESRCH);
+ error = proc_vmspace_ref(td, p, PRVM_BLOCK_EXEC | PRVM_CHECK_DEBUG,
+ &vm);
+ if (error != 0)
+ return (error);
if (SV_CURPROC_FLAG(SV_LP64))
l_map_str = l64_map_str;
@@ -1427,7 +1421,7 @@ linprocfs_doprocmaps(PFS_FILL_ARGS)
}
}
vm_map_unlock_read(map);
- vmspace_free(vm);
+ proc_vmspace_unref(td, p, PRVM_CHECK_DEBUG | PRVM_BLOCK_EXEC, vm);
return (error);
}
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
index 2ae10a487813..2414c76eb173 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -2006,6 +2006,7 @@ linux_prlimit64(struct thread *td, struct linux_prlimit64_args *args)
u_int which;
int flags;
int error;
+ bool exec_blocked;
if (args->new == NULL && args->old != NULL) {
if (linux_get_dummy_limit(args->resource, &rlim)) {
@@ -2033,6 +2034,7 @@ linux_prlimit64(struct thread *td, struct linux_prlimit64_args *args)
return (error);
}
+ exec_blocked = false;
flags = PGET_HOLD | PGET_NOTWEXIT;
if (args->new != NULL)
flags |= PGET_CANDEBUG;
@@ -2045,6 +2047,14 @@ linux_prlimit64(struct thread *td, struct linux_prlimit64_args *args)
error = pget(args->pid, flags, &p);
if (error != 0)
return (error);
+ exec_blocked = true;
+ PROC_LOCK(p);
+ execve_block_wait(td, p);
+ error = args->new != NULL ? p_candebug(td, p) :
+ p_cansee(td, p);
+ PROC_UNLOCK(p);
+ if (error != 0)
+ goto out;
}
if (args->old != NULL) {
PROC_LOCK(p);
@@ -2067,6 +2077,11 @@ linux_prlimit64(struct thread *td, struct linux_prlimit64_args *args)
error = kern_proc_setrlimit(td, p, which, &nrlim);
out:
+ if (exec_blocked) {
+ PROC_LOCK(p);
+ execve_unblock(td, p);
+ PROC_UNLOCK(p);
+ }
PRELE(p);
return (error);
}
diff --git a/sys/fs/cuse/cuse.c b/sys/fs/cuse/cuse.c
index d63a7d4691cf..b9423fd3543d 100644
--- a/sys/fs/cuse/cuse.c
+++ b/sys/fs/cuse/cuse.c
@@ -914,7 +914,7 @@ cuse_proc2proc_copy(struct proc *proc_s, vm_offset_t data_s,
};
PHOLD(proc_s);
- error = proc_rwmem(proc_s, &uio);
+ error = proc_rwmem(proc_s, &uio, 0);
PRELE(proc_s);
} else if (proc_cur == proc_s) {
@@ -933,7 +933,7 @@ cuse_proc2proc_copy(struct proc *proc_s, vm_offset_t data_s,
};
PHOLD(proc_d);
- error = proc_rwmem(proc_d, &uio);
+ error = proc_rwmem(proc_d, &uio, 0);
PRELE(proc_d);
} else {
error = EINVAL;
diff --git a/sys/fs/procfs/procfs_map.c b/sys/fs/procfs/procfs_map.c
index db6b225f6426..5ccef979856b 100644
--- a/sys/fs/procfs/procfs_map.c
+++ b/sys/fs/procfs/procfs_map.c
@@ -42,6 +42,7 @@
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/proc.h>
+#include <sys/ptrace.h>
#include <sys/resourcevar.h>
#include <sys/rwlock.h>
#include <sys/sbuf.h>
@@ -95,15 +96,14 @@ procfs_doprocmap(PFS_FILL_ARGS)
bool wrap32;
#endif
- PROC_LOCK(p);
- error = p_candebug(td, p);
- PROC_UNLOCK(p);
- if (error)
- return (error);
-
if (uio->uio_rw != UIO_READ)
return (EOPNOTSUPP);
+ error = proc_vmspace_ref(td, p, PRVM_BLOCK_EXEC | PRVM_CHECK_DEBUG,
+ &vm);
+ if (error != 0)
+ return (error);
+
#ifdef COMPAT_FREEBSD32
wrap32 = false;
if (SV_CURPROC_FLAG(SV_ILP32)) {
@@ -113,9 +113,6 @@ procfs_doprocmap(PFS_FILL_ARGS)
}
#endif
- vm = vmspace_acquire_ref(p);
- if (vm == NULL)
- return (ESRCH);
map = &vm->vm_map;
vm_map_lock_read(map);
VM_MAP_ENTRY_FOREACH(entry, map) {
@@ -240,6 +237,6 @@ procfs_doprocmap(PFS_FILL_ARGS)
}
}
vm_map_unlock_read(map);
- vmspace_free(vm);
+ proc_vmspace_unref(td, p, PRVM_BLOCK_EXEC | PRVM_CHECK_DEBUG, vm);
return (error);
}
diff --git a/sys/fs/procfs/procfs_mem.c b/sys/fs/procfs/procfs_mem.c
index c74954db37d6..5fc0c1acf140 100644
--- a/sys/fs/procfs/procfs_mem.c
+++ b/sys/fs/procfs/procfs_mem.c
@@ -61,11 +61,7 @@ procfs_doprocmem(PFS_FILL_ARGS)
if (uio->uio_resid == 0)
return (0);
- PROC_LOCK(p);
- error = p_candebug(td, p);
- PROC_UNLOCK(p);
- if (error == 0)
- error = proc_rwmem(p, uio);
+ error = proc_rwmem(p, uio, PRVM_CHECK_DEBUG | PRVM_BLOCK_EXEC);
return (error);
}
diff --git a/sys/fs/pseudofs/pseudofs_vnops.c b/sys/fs/pseudofs/pseudofs_vnops.c
index 8cd092118d0e..059e8fd0e8b2 100644
--- a/sys/fs/pseudofs/pseudofs_vnops.c
+++ b/sys/fs/pseudofs/pseudofs_vnops.c
@@ -37,6 +37,7 @@
#include <sys/ctype.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
+#include <sys/imgact.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/malloc.h>
@@ -132,6 +133,7 @@ static int
pfs_lookup_proc(pid_t pid, struct proc **p)
{
struct proc *proc;
+ struct thread *td;
proc = pfind(pid);
if (proc == NULL)
@@ -141,8 +143,10 @@ pfs_lookup_proc(pid_t pid, struct proc **p)
return (0);
}
_PHOLD(proc);
- PROC_UNLOCK(proc);
+ td = curthread;
+ execve_block_wait(td, proc);
*p = proc;
+ PROC_UNLOCK(proc);
return (1);
}
@@ -672,6 +676,7 @@ pfs_read(struct vop_read_args *va)
struct pfs_node *pn = pvd->pvd_pn;
struct uio *uio = va->a_uio;
struct proc *proc;
+ struct thread *td;
struct sbuf *sb = NULL;
int error, locked;
off_t buflen, buflim;
@@ -690,21 +695,30 @@ pfs_read(struct vop_read_args *va)
if (pn->pn_fill == NULL)
PFS_RETURN (EIO);
+ td = curthread;
+
/*
* This is necessary because either process' privileges may
* have changed since the open() call.
*/
- if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc))
+ if (!pfs_visible(td, pn, pvd->pvd_pid, &proc))
PFS_RETURN (EIO);
- if (proc != NULL) {
- _PHOLD(proc);
- PROC_UNLOCK(proc);
- }
vhold(vn);
locked = VOP_ISLOCKED(vn);
VOP_UNLOCK(vn);
+ if (proc != NULL) {
+ _PHOLD(proc);
+ execve_block_wait(td, proc);
+ if (!pfs_visible_proc(td, pn, proc)) {
+ PROC_UNLOCK(proc);
+ error = EIO;
+ goto ret;
+ }
+ PROC_UNLOCK(proc);
+ }
+
if (pn->pn_flags & PFS_RAWRD) {
PFS_TRACE(("%zd resid", uio->uio_resid));
error = pn_fill(curthread, proc, pn, NULL, uio);
@@ -774,8 +788,12 @@ pfs_read(struct vop_read_args *va)
ret:
vn_lock(vn, locked | LK_RETRY);
vdrop(vn);
- if (proc != NULL)
- PRELE(proc);
+ if (proc != NULL) {
+ PROC_LOCK(proc);
+ execve_unblock(td, proc);
+ _PRELE(proc);
+ PROC_UNLOCK(proc);
+ }
PFS_RETURN (error);
}
@@ -846,6 +864,7 @@ pfs_readdir(struct vop_readdir_args *va)
struct pfs_node *pd = pvd->pvd_pn;
pid_t pid = pvd->pvd_pid;
struct proc *p, *proc;
+ struct thread *td;
struct pfs_node *pn;
struct uio *uio;
struct pfsentry *pfsent, *pfsent2;
@@ -891,11 +910,13 @@ pfs_readdir(struct vop_readdir_args *va)
KASSERT(pid == NO_PID || proc != NULL,
("%s(): no process for pid %lu", __func__, (unsigned long)pid));
+ td = curthread;
if (pid != NO_PID) {
PROC_LOCK(proc);
/* check if the directory is visible to the caller */
if (!pfs_visible_proc(curthread, pd, proc)) {
+ execve_unblock(td, proc);
_PRELE(proc);
PROC_UNLOCK(proc);
pfs_unlock(pd);
@@ -955,6 +976,7 @@ pfs_readdir(struct vop_readdir_args *va)
resid -= PFS_DELEN;
}
if (proc != NULL) {
+ execve_unblock(td, proc);
_PRELE(proc);
PROC_UNLOCK(proc);
}
@@ -1079,6 +1101,7 @@ pfs_write(struct vop_write_args *va)
struct pfs_node *pn = pvd->pvd_pn;
struct uio *uio = va->a_uio;
struct proc *proc;
+ struct thread *td;
struct sbuf sb;
int error;
@@ -1098,36 +1121,44 @@ pfs_write(struct vop_write_args *va)
if (uio->uio_resid > PFS_MAXBUFSIZ)
PFS_RETURN (EIO);
+ td = curthread;
+
/*
* This is necessary because either process' privileges may
* have changed since the open() call.
*/
- if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc))
+ if (!pfs_visible(td, pn, pvd->pvd_pid, &proc))
PFS_RETURN (EIO);
if (proc != NULL) {
_PHOLD(proc);
+ execve_block_wait(td, proc);
+ if (!pfs_visible_proc(td, pn, proc)) {
+ PROC_UNLOCK(proc);
+ error = EIO;
+ goto out;
+ }
PROC_UNLOCK(proc);
}
if (pn->pn_flags & PFS_RAWWR) {
error = pn_fill(curthread, proc, pn, NULL, uio);
- if (proc != NULL)
- PRELE(proc);
- PFS_RETURN (error);
+ goto out;
}
sbuf_uionew(&sb, uio, &error);
- if (error) {
- if (proc != NULL)
- PRELE(proc);
- PFS_RETURN (error);
- }
+ if (error != 0)
+ goto out;
error = pn_fill(curthread, proc, pn, &sb, uio);
sbuf_delete(&sb);
- if (proc != NULL)
- PRELE(proc);
+out:
+ if (proc != NULL) {
+ PROC_LOCK(proc);
+ execve_unblock(td, proc);
+ _PRELE(proc);
+ PROC_UNLOCK(proc);
+ }
PFS_RETURN (error);
}
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index 920256f8c1bd..c937505a7270 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -50,6 +50,7 @@
#include <sys/filedesc.h>
#include <sys/filio.h>
#include <sys/fcntl.h>
+#include <sys/imgact.h>
#include <sys/kthread.h>
#include <sys/selinfo.h>
#include <sys/queue.h>
@@ -3038,10 +3039,6 @@ sysctl_kern_proc_kqueue(SYSCTL_HANDLER_ARGS)
if ((u_int)arg2 > 2 || (u_int)arg2 == 0)
return (EINVAL);
- error = pget((pid_t)name[0], PGET_HOLD | PGET_CANDEBUG, &p);
- if (error != 0)
- return (error);
-
td = curthread;
#ifdef COMPAT_FREEBSD32
compat32 = SV_CURPROC_FLAG(SV_ILP32);
@@ -3049,6 +3046,17 @@ sysctl_kern_proc_kqueue(SYSCTL_HANDLER_ARGS)
compat32 = false;
#endif
+ error = pget((pid_t)name[0], PGET_NOTWEXIT, &p);
+ if (error != 0)
+ return (error);
+
+ _PHOLD(p);
+ execve_block_wait(td, p);
+ error = p_candebug(td, p);
+ if (error != 0)
+ goto out1;
+ PROC_UNLOCK(p);
+
s = sbuf_new_for_sysctl(&sm, NULL, 0, req);
if (s == NULL) {
error = ENOMEM;
@@ -3069,7 +3077,11 @@ sysctl_kern_proc_kqueue(SYSCTL_HANDLER_ARGS)
sbuf_delete(s);
out:
- PRELE(p);
+ PROC_LOCK(p);
+out1:
+ execve_unblock(td, p);
+ _PRELE(p);
+ PROC_UNLOCK(p);
return (error);
}
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 349e13915b29..3cedf7ee36f5 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -26,7 +26,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
#include "opt_capsicum.h"
#include "opt_hwpmc_hooks.h"
#include "opt_ktrace.h"
@@ -45,6 +44,7 @@
#include <sys/imgact.h>
#include <sys/imgact_elf.h>
#include <sys/kernel.h>
+#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mman.h>
@@ -371,6 +371,77 @@ execve_nosetid(struct image_params *imgp)
}
/*
+ * Returns true if the execblock was obtained, in this case the
+ * process lock is kept. Returns false if the execblock was not
+ * obtained, but the function slept and the lock was dropped.
+ */
+bool
+execve_block(struct thread *td, struct proc *p)
+{
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ MPASS(td == curthread);
+ MPASS(p != td->td_proc || (p->p_flag & P_INEXEC) == 0);
+
+ if (p != td->td_proc && (p->p_flag & P_INEXEC) != 0) {
+ p->p_flag2 |= P2_INEXEC_WAIT;
+ msleep(&p->p_execblock, &p->p_mtx, PDROP, "inexec", 0);
+ return (false);
+ }
+ MPASS(p->p_execblock < UINT_MAX);
+ p->p_execblock++;
+ return (true);
+}
+
+/*
+ * Might drop the process lock internally, callers must re-check the
+ * invariants afterward.
+ */
+void
+execve_block_wait(struct thread *td, struct proc *p)
+{
+ bool first;
+
+ PROC_ASSERT_HELD(p);
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+
+ for (first = true;; first = false) {
+ if (!first)
+ PROC_LOCK(p);
+ if (execve_block(td, p))
+ return;
+ }
+}
+
+void
+execve_unblock(struct thread *td, struct proc *p)
+{
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ MPASS(td == curthread);
+
+ MPASS(p->p_execblock > 0);
+ p->p_execblock--;
+ if (p->p_execblock == 0 && (p->p_flag2 & P2_INEXEC_WAIT) != 0) {
+ p->p_flag2 &= ~P2_INEXEC_WAIT;
+ wakeup(&p->p_execblock);
+ }
+}
+
+void
+execve_block_pass(struct thread *td)
+{
+ struct proc *p;
+
+ MPASS(td == curthread);
+ p = td->td_proc;
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+
+ while (p->p_execblock != 0) {
+ p->p_flag2 |= P2_INEXEC_WAIT;
+ msleep(&p->p_execblock, &p->p_mtx, 0, "exeblk", 0);
+ }
+}
+
+/*
* In-kernel implementation of execve(). All arguments are assumed to be
* userspace pointers from the passed thread.
*/
@@ -425,6 +496,7 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
PROC_LOCK(p);
KASSERT((p->p_flag & P_INEXEC) == 0,
("%s(): process already has P_INEXEC flag", __func__));
+ execve_block_pass(td);
p->p_flag |= P_INEXEC;
PROC_UNLOCK(p);
@@ -893,7 +965,11 @@ interpret:
* as we're now a bona fide freshly-execed process.
*/
KNOTE_LOCKED(p->p_klist, NOTE_EXEC);
+ MPASS(p->p_execblock == 0);
+ if ((p->p_flag2 & P2_INEXEC_WAIT) != 0)
+ wakeup(&p->p_execblock);
p->p_flag &= ~P_INEXEC;
+ p->p_flag2 &= ~P2_INEXEC_WAIT;
/* clear "fork but no exec" flag, as we _are_ execing */
p->p_acflag &= ~AFORK;
@@ -975,7 +1051,10 @@ exec_fail_dealloc:
exec_fail:
/* we're done here, clear P_INEXEC */
PROC_LOCK(p);
+ if ((p->p_flag2 & P2_INEXEC_WAIT) != 0)
+ wakeup(&p->p_execblock);
p->p_flag &= ~P_INEXEC;
+ p->p_flag2 &= ~P2_INEXEC_WAIT;
PROC_UNLOCK(p);
SDT_PROBE1(proc, , , exec__failure, error);
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index bbd30dfb9480..baa5cad2bdc8 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -325,6 +325,7 @@ exit1(struct thread *td, int rval, int signo)
while (p->p_lock > 0)
msleep(&p->p_lock, &p->p_mtx, PWAIT, "exithold", 0);
+ MPASS(p->p_execblock == 0);
PROC_UNLOCK(p);
/* Drain the limit callout while we don't have the proc locked */
callout_drain(&p->p_limco);
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 7731ae4d14f3..d8578f01677b 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -384,6 +384,7 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
bzero(&p2->p_startzero,
__rangeof(struct proc, p_startzero, p_endzero));
+ p2->p_execblock = 0;
/* Tell the prison that we exist. */
prison_proc_hold(p2->p_ucred->cr_prison);
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index c4d5356bb292..061a9f7fa4dd 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -45,6 +45,7 @@
#include <sys/eventhandler.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
+#include <sys/imgact.h>
#include <sys/ipc.h>
#include <sys/jail.h>
#include <sys/kernel.h>
@@ -1833,8 +1834,8 @@ pargs_drop(struct pargs *pa)
}
static int
-proc_read_string(struct thread *td, struct proc *p, const char *sptr, char *buf,
- size_t len)
+proc_read_string(struct thread *td, struct vmspace *vm, const char *sptr,
+ char *buf, size_t len)
{
ssize_t n;
@@ -1843,7 +1844,7 @@ proc_read_string(struct thread *td, struct proc *p, const char *sptr, char *buf,
* and is aligned at the end of the page, and the following page is not
* mapped.
*/
- n = proc_readmem(td, p, (vm_offset_t)sptr, buf, len);
+ n = vmspace_iop(td, vm, (vm_offset_t)sptr, buf, len, UIO_READ);
if (n <= 0)
return (ENOMEM);
return (0);
@@ -1859,8 +1860,8 @@ enum proc_vector_type {
#ifdef COMPAT_FREEBSD32
static int
-get_proc_vector32(struct thread *td, struct proc *p, char ***proc_vectorp,
- size_t *vsizep, enum proc_vector_type type)
+get_proc_vector32(struct thread *td, struct proc *p, struct vmspace *vm,
+ char ***proc_vectorp, size_t *vsizep, enum proc_vector_type type)
{
struct freebsd32_ps_strings pss;
Elf32_Auxinfo aux;
@@ -1871,8 +1872,8 @@ get_proc_vector32(struct thread *td, struct proc *p, char ***proc_vectorp,
int i, error;
error = 0;
- if (proc_readmem(td, p, PROC_PS_STRINGS(p), &pss, sizeof(pss)) !=
- sizeof(pss))
+ if (vmspace_iop(td, vm, PROC_PS_STRINGS(p), &pss, sizeof(pss),
+ UIO_READ) != sizeof(pss))
return (ENOMEM);
switch (type) {
case PROC_ARG:
@@ -1895,8 +1896,8 @@ get_proc_vector32(struct thread *td, struct proc *p, char ***proc_vectorp,
if (vptr % 4 != 0)
return (ENOEXEC);
for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) {
- if (proc_readmem(td, p, ptr, &aux, sizeof(aux)) !=
- sizeof(aux))
+ if (vmspace_iop(td, vm, ptr, &aux, sizeof(aux),
+ UIO_READ) != sizeof(aux))
return (ENOMEM);
if (aux.a_type == AT_NULL)
break;
@@ -1912,7 +1913,7 @@ get_proc_vector32(struct thread *td, struct proc *p, char ***proc_vectorp,
return (EINVAL);
}
proc_vector32 = malloc(size, M_TEMP, M_WAITOK);
- if (proc_readmem(td, p, vptr, proc_vector32, size) != size) {
+ if (vmspace_iop(td, vm, vptr, proc_vector32, size, UIO_READ) != size) {
error = ENOMEM;
goto done;
}
@@ -1933,8 +1934,8 @@ done:
#endif
static int
-get_proc_vector(struct thread *td, struct proc *p, char ***proc_vectorp,
- size_t *vsizep, enum proc_vector_type type)
+get_proc_vector(struct thread *td, struct proc *p, struct vmspace *vm,
+ char ***proc_vectorp, size_t *vsizep, enum proc_vector_type type)
{
struct ps_strings pss;
Elf_Auxinfo aux;
@@ -1944,11 +1945,13 @@ get_proc_vector(struct thread *td, struct proc *p, char ***proc_vectorp,
int i;
#ifdef COMPAT_FREEBSD32
- if (SV_PROC_FLAG(p, SV_ILP32) != 0)
- return (get_proc_vector32(td, p, proc_vectorp, vsizep, type));
+ if (SV_PROC_FLAG(p, SV_ILP32) != 0) {
+ return (get_proc_vector32(td, p, vm, proc_vectorp,
+ vsizep, type));
+ }
#endif
- if (proc_readmem(td, p, PROC_PS_STRINGS(p), &pss, sizeof(pss)) !=
- sizeof(pss))
+ if (vmspace_iop(td, vm, PROC_PS_STRINGS(p), &pss, sizeof(pss),
+ UIO_READ) != sizeof(pss))
return (ENOMEM);
switch (type) {
case PROC_ARG:
@@ -1986,8 +1989,8 @@ get_proc_vector(struct thread *td, struct proc *p, char ***proc_vectorp,
* to the allocated proc_vector.
*/
for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) {
- if (proc_readmem(td, p, ptr, &aux, sizeof(aux)) !=
- sizeof(aux))
+ if (vmspace_iop(td, vm, ptr, &aux, sizeof(aux),
+ UIO_READ) != sizeof(aux))
return (ENOMEM);
if (aux.a_type == AT_NULL)
break;
@@ -2009,7 +2012,7 @@ get_proc_vector(struct thread *td, struct proc *p, char ***proc_vectorp,
return (EINVAL); /* In case we are built without INVARIANTS. */
}
proc_vector = malloc(size, M_TEMP, M_WAITOK);
- if (proc_readmem(td, p, vptr, proc_vector, size) != size) {
+ if (vmspace_iop(td, vm, vptr, proc_vector, size, UIO_READ) != size) {
free(proc_vector, M_TEMP);
return (ENOMEM);
}
@@ -2025,6 +2028,7 @@ static int
get_ps_strings(struct thread *td, struct proc *p, struct sbuf *sb,
enum proc_vector_type type)
{
+ struct vmspace *vm;
size_t done, len, nchr, vsize;
int error, i;
char **proc_vector, *sptr;
@@ -2037,9 +2041,14 @@ get_ps_strings(struct thread *td, struct proc *p, struct sbuf *sb,
*/
nchr = 2 * (PATH_MAX + ARG_MAX);
- error = get_proc_vector(td, p, &proc_vector, &vsize, type);
+ error = proc_vmspace_ref(td, p, PRVM_BLOCK_EXEC |
+ PRVM_CHECK_VISIBILITY, &vm);
if (error != 0)
return (error);
+
+ error = get_proc_vector(td, p, vm, &proc_vector, &vsize, type);
+ if (error != 0)
+ goto out;
for (done = 0, i = 0; i < (int)vsize && done < nchr; i++) {
/*
* The program may have scribbled into its argv array, e.g. to
@@ -2049,7 +2058,7 @@ get_ps_strings(struct thread *td, struct proc *p, struct sbuf *sb,
if (proc_vector[i] == NULL)
break;
for (sptr = proc_vector[i]; ; sptr += GET_PS_STRINGS_CHUNK_SZ) {
- error = proc_read_string(td, p, sptr, pss_string,
+ error = proc_read_string(td, vm, sptr, pss_string,
sizeof(pss_string));
if (error != 0)
goto done;
@@ -2066,6 +2075,8 @@ get_ps_strings(struct thread *td, struct proc *p, struct sbuf *sb,
}
done:
free(proc_vector, M_TEMP);
+out:
+ proc_vmspace_unref(td, p, PRVM_BLOCK_EXEC | PRVM_CHECK_VISIBILITY, vm);
return (error);
}
@@ -2086,11 +2097,17 @@ proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb)
int
proc_getauxv(struct thread *td, struct proc *p, struct sbuf *sb)
{
+ struct vmspace *vm;
size_t vsize, size;
char **auxv;
int error;
- error = get_proc_vector(td, p, &auxv, &vsize, PROC_AUX);
+ error = proc_vmspace_ref(td, p, PRVM_BLOCK_EXEC | PRVM_CHECK_DEBUG,
+ &vm);
+ if (error != 0)
+ return (error);
+ error = get_proc_vector(td, p, vm, &auxv, &vsize, PROC_AUX);
+ proc_vmspace_unref(td, p, PRVM_BLOCK_EXEC | PRVM_CHECK_DEBUG, vm);
if (error == 0) {
#ifdef COMPAT_FREEBSD32
if (SV_PROC_FLAG(p, SV_ILP32) != 0)
@@ -2403,6 +2420,7 @@ sysctl_kern_proc_ovmmap(SYSCTL_HANDLER_ARGS)
int error, *name;
struct vnode *vp;
struct proc *p;
+ struct thread *td;
vm_map_t map;
struct vmspace *vm;
@@ -2411,11 +2429,12 @@ sysctl_kern_proc_ovmmap(SYSCTL_HANDLER_ARGS)
return (EINVAL);
name = (int *)arg1;
+ td = curthread;
error = pget((pid_t)name[0], PGET_WANTREAD, &p);
if (error != 0)
return (error);
- vm = vmspace_acquire_ref(p);
- if (vm == NULL) {
+ error = proc_vmspace_ref(td, p, PRVM_CHECK_DEBUG, &vm);
+ if (error != 0) {
PRELE(p);
return (ESRCH);
}
@@ -2527,7 +2546,7 @@ sysctl_kern_proc_ovmmap(SYSCTL_HANDLER_ARGS)
}
}
vm_map_unlock_read(map);
- vmspace_free(vm);
+ proc_vmspace_unref(td, p, PRVM_CHECK_DEBUG, vm);
PRELE(p);
free(kve, M_TEMP);
return (error);
@@ -2618,6 +2637,7 @@ kern_proc_vmmap_out(struct proc *p, struct sbuf *sb, ssize_t maxlen, int flags)
struct vmspace *vm;
struct cdev *cdev;
struct cdevsw *csw;
+ struct thread *td;
vm_offset_t addr;
unsigned int last_timestamp;
int error, ref;
@@ -2629,10 +2649,11 @@ kern_proc_vmmap_out(struct proc *p, struct sbuf *sb, ssize_t maxlen, int flags)
_PHOLD(p);
PROC_UNLOCK(p);
- vm = vmspace_acquire_ref(p);
- if (vm == NULL) {
+ td = curthread;
+ error = proc_vmspace_ref(td, p, PRVM_CHECK_DEBUG, &vm);
+ if (error != 0) {
PRELE(p);
- return (ESRCH);
+ return (error);
}
kve = malloc(sizeof(*kve), M_TEMP, M_WAITOK | M_ZERO);
@@ -2747,7 +2768,7 @@ kern_proc_vmmap_out(struct proc *p, struct sbuf *sb, ssize_t maxlen, int flags)
if (vp != NULL) {
vn_fullpath(vp, &fullpath, &freepath);
kve->kve_vn_type = vntype_to_kinfo(vp->v_type);
- cred = curthread->td_ucred;
+ cred = td->td_ucred;
vn_lock(vp, LK_SHARED | LK_RETRY);
if (VOP_GETATTR(vp, &va, cred) == 0) {
kve->kve_vn_fileid = va.va_fileid;
@@ -2803,7 +2824,7 @@ kern_proc_vmmap_out(struct proc *p, struct sbuf *sb, ssize_t maxlen, int flags)
}
}
vm_map_unlock_read(map);
- vmspace_free(vm);
+ proc_vmspace_unref(td, p, PRVM_CHECK_DEBUG, vm);
PRELE(p);
free(kve, M_TEMP);
return (error);
@@ -2842,7 +2863,7 @@ sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS)
struct kinfo_kstack *kkstp;
int error, i, *name, numthreads;
lwpid_t *lwpidarray;
- struct thread *td;
+ struct thread *td, *ctd;
struct stack *st;
struct sbuf sb;
struct proc *p;
@@ -2853,7 +2874,8 @@ sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS)
return (EINVAL);
name = (int *)arg1;
- error = pget((pid_t)name[0], PGET_NOTINEXEC | PGET_WANTREAD, &p);
+ ctd = curthread;
+ error = pget((pid_t)name[0], PGET_WANTREAD, &p);
if (error != 0)
return (error);
@@ -2862,6 +2884,14 @@ sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS)
lwpidarray = NULL;
PROC_LOCK(p);
+ execve_block_wait(ctd, p);
+ error = p_candebug(ctd, p);
+ if (error != 0) {
+ execve_unblock(ctd, p);
+ _PRELE(p);
+ PROC_UNLOCK(p);
+ return (error);
+ }
do {
if (lwpidarray != NULL) {
free(lwpidarray, M_TEMP);
@@ -2874,15 +2904,6 @@ sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS)
PROC_LOCK(p);
} while (numthreads < p->p_numthreads);
- /*
- * XXXRW: During the below loop, execve(2) and countless other sorts
- * of changes could have taken place. Should we check to see if the
- * vmspace has been replaced, or the like, in order to prevent
- * giving a snapshot that spans, say, execve(2), with some threads
- * before and some after? Among other things, the credentials could
- * have changed, in which case the right to extract debug info might
- * no longer be assured.
- */
i = 0;
FOREACH_THREAD_IN_PROC(p, td) {
KASSERT(i < numthreads,
@@ -2917,7 +2938,10 @@ sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS)
if (error)
break;
}
- PRELE(p);
+ PROC_LOCK(p);
+ execve_unblock(ctd, p);
+ _PRELE(p);
+ PROC_UNLOCK(p);
if (lwpidarray != NULL)
free(lwpidarray, M_TEMP);
stack_destroy(st);
@@ -2970,8 +2994,9 @@ sysctl_kern_proc_rlimit(SYSCTL_HANDLER_ARGS)
u_int namelen = arg2;
struct rlimit rlim;
struct proc *p;
+ struct thread *td;
u_int which;
- int flags, error;
+ int error;
if (namelen != 2)
return (EINVAL);
@@ -2983,23 +3008,24 @@ sysctl_kern_proc_rlimit(SYSCTL_HANDLER_ARGS)
if (req->newptr != NULL && req->newlen != sizeof(rlim))
return (EINVAL);
- flags = PGET_HOLD | PGET_NOTWEXIT;
- if (req->newptr != NULL)
- flags |= PGET_CANDEBUG;
- else
- flags |= PGET_CANSEE;
- error = pget((pid_t)name[0], flags, &p);
+ td = curthread;
+ error = pget((pid_t)name[0], PGET_NOTWEXIT, &p);
if (error != 0)
return (error);
+ _PHOLD(p);
+ execve_block_wait(td, p);
+ error = req->newptr != NULL ? p_candebug(td, p) : p_cansee(td, p);
+ if (error != 0)
+ goto errout1;
/*
* Retrieve limit.
*/
if (req->oldptr != NULL) {
- PROC_LOCK(p);
lim_rlimit_proc(p, which, &rlim);
- PROC_UNLOCK(p);
}
+ PROC_UNLOCK(p);
+
error = SYSCTL_OUT(req, &rlim, sizeof(rlim));
if (error != 0)
goto errout;
@@ -3014,7 +3040,11 @@ sysctl_kern_proc_rlimit(SYSCTL_HANDLER_ARGS)
}
errout:
- PRELE(p);
+ PROC_LOCK(p);
+errout1:
+ _PRELE(p);
+ execve_unblock(td, p);
+ PROC_UNLOCK(p);
return (error);
}
@@ -3103,39 +3133,38 @@ sysctl_kern_proc_osrel(SYSCTL_HANDLER_ARGS)
int *name = (int *)arg1;
u_int namelen = arg2;
struct proc *p;
- int flags, error, osrel;
+ int flags, error, old_osrel, osrel;
if (namelen != 1)
return (EINVAL);
- if (req->newptr != NULL && req->newlen != sizeof(osrel))
- return (EINVAL);
-
- flags = PGET_HOLD | PGET_NOTWEXIT;
- if (req->newptr != NULL)
+ flags = PGET_NOTWEXIT;
+ if (req->newptr != NULL) {
+ if (req->newlen != sizeof(osrel))
+ return (EINVAL);
+ error = SYSCTL_IN(req, &osrel, sizeof(osrel));
+ if (error != 0)
+ return (error);
+ if (osrel < 0)
+ return (EINVAL);
flags |= PGET_CANDEBUG;
- else
+ } else {
flags |= PGET_CANSEE;
+ }
error = pget((pid_t)name[0], flags, &p);
if (error != 0)
return (error);
-
- error = SYSCTL_OUT(req, &p->p_osrel, sizeof(p->p_osrel));
- if (error != 0)
- goto errout;
-
- if (req->newptr != NULL) {
- error = SYSCTL_IN(req, &osrel, sizeof(osrel));
- if (error != 0)
- goto errout;
- if (osrel < 0) {
- error = EINVAL;
- goto errout;
- }
- p->p_osrel = osrel;
+ if ((p->p_flag & P_INEXEC) != 0) {
+ error = EBUSY;
+ } else {
+ old_osrel = p->p_osrel;
+ if (req->newptr != NULL)
+ p->p_osrel = osrel;
}
-errout:
- PRELE(p);
+ PROC_UNLOCK(p);
+
+ if (error == 0)
+ error = SYSCTL_OUT(req, &old_osrel, sizeof(old_osrel));
return (error);
}
@@ -3272,6 +3301,7 @@ sysctl_kern_proc_vm_layout(SYSCTL_HANDLER_ARGS)
{
struct kinfo_vm_layout kvm;
struct proc *p;
+ struct thread *td;
struct vmspace *vmspace;
int error, *name;
@@ -3279,6 +3309,7 @@ sysctl_kern_proc_vm_layout(SYSCTL_HANDLER_ARGS)
if ((u_int)arg2 != 1)
return (EINVAL);
+ td = curthread;
error = pget((pid_t)name[0], PGET_CANDEBUG, &p);
if (error != 0)
return (error);
@@ -3290,8 +3321,13 @@ sysctl_kern_proc_vm_layout(SYSCTL_HANDLER_ARGS)
}
}
#endif
- vmspace = vmspace_acquire_ref(p);
+ _PHOLD(p);
PROC_UNLOCK(p);
+ error = proc_vmspace_ref(td, p, PRVM_CHECK_DEBUG, &vmspace);
+ if (error != 0) {
+ PRELE(p);
+ return (error);
+ }
memset(&kvm, 0, sizeof(kvm));
kvm.kvm_min_user_addr = vm_map_min(&vmspace->vm_map);
@@ -3343,7 +3379,8 @@ sysctl_kern_proc_vm_layout(SYSCTL_HANDLER_ARGS)
#ifdef COMPAT_FREEBSD32
out:
#endif
- vmspace_free(vmspace);
+ proc_vmspace_unref(td, p, PRVM_CHECK_DEBUG, vmspace);
+ PRELE(p);
return (error);
}
diff --git a/sys/kern/kern_procctl.c b/sys/kern/kern_procctl.c
index 23b3403fec4f..bdcb4d544cc6 100644
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -40,6 +40,7 @@
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/procctl.h>
+#include <sys/ptrace.h>
#include <sys/sx.h>
#include <sys/syscallsubr.h>
#include <sys/sysproto.h>
@@ -872,37 +873,41 @@ wxmap_ctl(struct thread *td, struct proc *p, void *data)
{
struct vmspace *vm;
vm_map_t map;
- int state;
+ int error, state;
PROC_LOCK_ASSERT(p, MA_OWNED);
if ((p->p_flag & P_WEXIT) != 0)
return (ESRCH);
state = *(int *)data;
+ error = 0;
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) {
+ error = proc_vmspace_ref(td, p, PRVM_BLOCK_EXEC |
+ PRVM_CHECK_DEBUG, &vm);
+ if (error == 0) {
map = &vm->vm_map;
vm_map_lock(map);
map->flags &= ~MAP_WXORX;
vm_map_unlock(map);
- vmspace_free(vm);
+ PROC_LOCK(p);
+ p->p_flag2 |= P2_WXORX_DISABLE;
+ PROC_UNLOCK(p);
+ proc_vmspace_unref(td, p, PRVM_BLOCK_EXEC |
+ PRVM_CHECK_DEBUG, vm);
}
PROC_LOCK(p);
- _PRELE(p);
break;
case PROC_WX_MAPPINGS_DISALLOW_EXEC:
p->p_flag2 |= P2_WXORX_ENABLE_EXEC;
break;
default:
- return (EINVAL);
+ error = EINVAL;
+ break;
}
- return (0);
+ return (error);
}
static int
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
index e2accd7f7729..8b5e06820b2c 100644
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -51,6 +51,7 @@
#include <sys/systm.h>
#include <sys/abi_compat.h>
#include <sys/acct.h>
+#include <sys/imgact.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
@@ -1010,6 +1011,8 @@ sys_seteuid(struct thread *td, struct seteuid_args *uap)
newcred = crget();
euip = uifind(euid);
PROC_LOCK(p);
+ execve_block_pass(td);
+
/*
* Copy credentials so other references do not see our changes.
*/
@@ -1064,6 +1067,7 @@ sys_setgid(struct thread *td, struct setgid_args *uap)
AUDIT_ARG_GID(gid);
newcred = crget();
PROC_LOCK(p);
+ execve_block_pass(td);
oldcred = crcopysafe(p, newcred);
#ifdef MAC
@@ -1162,6 +1166,7 @@ sys_setegid(struct thread *td, struct setegid_args *uap)
AUDIT_ARG_EGID(egid);
newcred = crget();
PROC_LOCK(p);
+ execve_block_pass(td);
oldcred = crcopysafe(p, newcred);
#ifdef MAC
@@ -1257,6 +1262,7 @@ kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups)
if (ngrp != 0)
crextend(newcred, ngrp);
PROC_LOCK(p);
+ execve_block_pass(td);
oldcred = crcopysafe(p, newcred);
#ifdef MAC
@@ -1319,6 +1325,7 @@ sys_setreuid(struct thread *td, struct setreuid_args *uap)
euip = uifind(euid);
ruip = uifind(ruid);
PROC_LOCK(p);
+ execve_block_pass(td);
oldcred = crcopysafe(p, newcred);
#ifdef MAC
@@ -1398,6 +1405,7 @@ sys_setregid(struct thread *td, struct setregid_args *uap)
AUDIT_ARG_RGID(rgid);
newcred = crget();
PROC_LOCK(p);
+ execve_block_pass(td);
oldcred = crcopysafe(p, newcred);
#ifdef MAC
@@ -1468,6 +1476,7 @@ sys_setresuid(struct thread *td, struct setresuid_args *uap)
euip = uifind(euid);
ruip = uifind(ruid);
PROC_LOCK(p);
+ execve_block_pass(td);
oldcred = crcopysafe(p, newcred);
#ifdef MAC
@@ -1559,6 +1568,7 @@ sys_setresgid(struct thread *td, struct setresgid_args *uap)
AUDIT_ARG_SGID(sgid);
newcred = crget();
PROC_LOCK(p);
+ execve_block_pass(td);
oldcred = crcopysafe(p, newcred);
#ifdef MAC
@@ -2310,11 +2320,11 @@ p_candebug(struct thread *td, struct proc *p)
}
/*
- * Can't trace a process that's currently exec'ing.
- *
- * XXX: Note, this is not a security policy decision, it's a
- * basic correctness/functionality decision. Therefore, this check
- * should be moved to the caller's of p_candebug().
+ * Can't trace a process that's currently exec'ing. Otherwise
+ * the process vmspace might change, and the target might be
+ * loading a setugid image. The execve_block(9) and
+ * proc_vmspace_ref(9) allow to get the stable credentials and
+ * vmspace reference.
*/
if ((p->p_flag & P_INEXEC) != 0)
return (EBUSY);
diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c
index 9729bf987aa6..80f3588f2614 100644
--- a/sys/kern/kern_resource.c
+++ b/sys/kern/kern_resource.c
@@ -48,6 +48,7 @@
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
+#include <sys/ptrace.h>
#include <sys/refcount.h>
#include <sys/racct.h>
#include <sys/resourcevar.h>
@@ -810,11 +811,11 @@ sys_getrlimit(struct thread *td, struct getrlimit_args *uap)
}
static int
-getrlimitusage_one(struct proc *p, u_int which, int flags, rlim_t *res)
+getrlimitusage_one(struct proc *p, struct vmspace *vm, u_int which, int flags,
+ rlim_t *res)
{
struct thread *td;
struct uidinfo *ui;
- struct vmspace *vm;
uid_t uid;
int error;
@@ -825,7 +826,6 @@ getrlimitusage_one(struct proc *p, u_int which, int flags, rlim_t *res)
PROC_UNLOCK(p);
ui = uifind(uid);
- vm = vmspace_acquire_ref(p);
switch (which) {
case RLIMIT_CPU:
@@ -903,7 +903,6 @@ getrlimitusage_one(struct proc *p, u_int which, int flags, rlim_t *res)
break;
}
- vmspace_free(vm);
uifree(ui);
return (error);
}
@@ -911,12 +910,15 @@ getrlimitusage_one(struct proc *p, u_int which, int flags, rlim_t *res)
int
sys_getrlimitusage(struct thread *td, struct getrlimitusage_args *uap)
{
+ struct proc *p;
rlim_t res;
int error;
if ((uap->flags & ~(GETRLIMITUSAGE_EUID)) != 0)
return (EINVAL);
- error = getrlimitusage_one(curproc, uap->which, uap->flags, &res);
+ p = curproc;
+ error = getrlimitusage_one(p, p->p_vmspace, uap->which, uap->flags,
+ &res);
if (error == 0)
error = copyout(&res, uap->res, sizeof(res));
return (error);
@@ -1750,6 +1752,8 @@ sysctl_kern_proc_rlimit_usage(SYSCTL_HANDLER_ARGS)
{
rlim_t resval[RLIM_NLIMITS];
struct proc *p;
+ struct thread *td;
+ struct vmspace *vm;
size_t len;
int error, *name, i;
@@ -1759,15 +1763,20 @@ sysctl_kern_proc_rlimit_usage(SYSCTL_HANDLER_ARGS)
if (req->newptr != NULL)
return (EINVAL);
- error = pget((pid_t)name[0], PGET_WANTREAD, &p);
+ td = curthread;
+ error = pget((pid_t)name[0], PGET_HOLD | PGET_NOTWEXIT, &p);
if (error != 0)
return (error);
+ error = proc_vmspace_ref(td, p, PRVM_BLOCK_EXEC |
+ PRVM_CHECK_VISIBILITY, &vm);
+ if (error != 0)
+ goto out;
if ((u_int)arg2 == 1) {
len = sizeof(resval);
memset(resval, 0, sizeof(resval));
for (i = 0; i < RLIM_NLIMITS; i++) {
- error = getrlimitusage_one(p, (unsigned)i, 0,
+ error = getrlimitusage_one(p, vm, (unsigned)i, 0,
&resval[i]);
if (error == ENXIO) {
resval[i] = -1;
@@ -1778,7 +1787,7 @@ sysctl_kern_proc_rlimit_usage(SYSCTL_HANDLER_ARGS)
}
} else {
len = sizeof(resval[0]);
- error = getrlimitusage_one(p, (unsigned)name[1], 0,
+ error = getrlimitusage_one(p, vm, (unsigned)name[1], 0,
&resval[0]);
if (error == ENXIO) {
resval[0] = -1;
@@ -1787,6 +1796,8 @@ sysctl_kern_proc_rlimit_usage(SYSCTL_HANDLER_ARGS)
}
if (error == 0)
error = SYSCTL_OUT(req, resval, len);
+ proc_vmspace_unref(td, p, PRVM_BLOCK_EXEC | PRVM_CHECK_VISIBILITY, vm);
+out:
PRELE(p);
return (error);
}
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index 3fb4595bb5f7..def87f8a1cd5 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -34,6 +34,7 @@
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/imgact.h>
#include <sys/ktr.h>
#include <sys/limits.h>
#include <sys/lock.h>
@@ -345,25 +346,93 @@ proc_sstep(struct thread *td)
PROC_ACTION(ptrace_single_step(td));
}
+static int
+proc_vmspace_check_access(struct thread *td, struct proc *p, int flags)
+{
+ PROC_ASSERT_HELD(p);
+ if ((flags & PRVM_CHECK_DEBUG) != 0)
+ return (p_candebug(td, p));
+ if ((flags & PRVM_CHECK_VISIBILITY) != 0)
+ return (p_cansee(td, p));
+ return (0);
+}
+
int
-proc_rwmem(struct proc *p, struct uio *uio)
+proc_vmspace_ref(struct thread *td, struct proc *p, int flags,
+ struct vmspace **vmp)
+{
+ struct vmspace *vm;
+ int error;
+
+ MPASS((flags & ~(PRVM_BLOCK_EXEC | PRVM_CHECK_VISIBILITY |
+ PRVM_CHECK_DEBUG)) == 0);
+ MPASS((flags & (PRVM_CHECK_VISIBILITY | PRVM_CHECK_DEBUG)) !=
+ (PRVM_CHECK_VISIBILITY | PRVM_CHECK_DEBUG));
+
+ PROC_LOCK(p);
+ if (p != td->td_proc) {
+ PROC_ASSERT_HELD(p);
+
+ /*
+ * Make sure that the vmspace doesn't switch out from
+ * under us.
+ */
+ if ((flags & PRVM_BLOCK_EXEC) != 0) {
+ for (;;) {
+ if (!execve_block(td, p)) {
+ PROC_LOCK(p);
+ continue;
+ }
+ error = proc_vmspace_check_access(td, p, flags);
+ if (error != 0) {
+ execve_unblock(td, p);
+ PROC_UNLOCK(p);
+ return (error);
+ }
+ break;
+ }
+ } else {
+ error = proc_vmspace_check_access(td, p, flags);
+ if (error != 0) {
+ PROC_UNLOCK(p);
+ return (error);
+ }
+ }
+ }
+ vm = vmspace_acquire_ref(p);
+ if (vm == NULL) {
+ if (p != td->td_proc && (flags & PRVM_BLOCK_EXEC) != 0)
+ execve_unblock(td, p);
+ PROC_UNLOCK(p);
+ return (ESRCH);
+ }
+ PROC_UNLOCK(p);
+ *vmp = vm;
+ return (0);
+}
+
+void
+proc_vmspace_unref(struct thread *td, struct proc *p, int flags,
+ struct vmspace *vm)
+{
+ vmspace_free(vm);
+ if (p != td->td_proc && (flags & PRVM_BLOCK_EXEC) != 0) {
+ PROC_LOCK(p);
+ PROC_ASSERT_HELD(p);
+ execve_unblock(td, p);
+ PROC_UNLOCK(p);
+ }
+}
+
+static int
+vmspace_rwmem(struct vmspace *vm, struct uio *uio)
{
vm_map_t map;
vm_offset_t pageno; /* page number */
vm_prot_t reqprot;
int error, fault_flags, page_offset, writing;
- /*
- * Make sure that the process' vmspace remains live.
- */
- if (p != curproc)
- PROC_ASSERT_HELD(p);
- PROC_LOCK_ASSERT(p, MA_NOTOWNED);
-
- /*
- * The map we want...
- */
- map = &p->p_vmspace->vm_map;
+ map = &vm->vm_map;
/*
* If we are writing, then we request vm_fault() to create a private
@@ -432,13 +501,30 @@ proc_rwmem(struct proc *p, struct uio *uio)
return (error);
}
-static ssize_t
-proc_iop(struct thread *td, struct proc *p, vm_offset_t va, void *buf,
+int
+proc_rwmem(struct proc *p, struct uio *uio, int flags)
+{
+ struct vmspace *vm;
+ struct thread *td;
+ int error;
+
+ td = curthread;
+ error = proc_vmspace_ref(td, p, flags, &vm);
+ if (error != 0)
+ return (error);
+ error = vmspace_rwmem(vm, uio);
+ proc_vmspace_unref(td, p, flags, vm);
+ return (error);
+}
+
+ssize_t
+vmspace_iop(struct thread *td, struct vmspace *vm, vm_offset_t va, void *buf,
size_t len, enum uio_rw rw)
{
struct iovec iov;
struct uio uio;
ssize_t slen;
+ int error;
MPASS(len < SSIZE_MAX);
slen = (ssize_t)len;
@@ -452,8 +538,8 @@ proc_iop(struct thread *td, struct proc *p, vm_offset_t va, void *buf,
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = rw;
uio.uio_td = td;
- proc_rwmem(p, &uio);
- if (uio.uio_resid == slen)
+ error = vmspace_rwmem(vm, &uio);
+ if (error != 0 || uio.uio_resid == slen)
return (-1);
return (slen - uio.uio_resid);
}
@@ -463,7 +549,7 @@ proc_readmem(struct thread *td, struct proc *p, vm_offset_t va, void *buf,
size_t len)
{
- return (proc_iop(td, p, va, buf, len, UIO_READ));
+ return (vmspace_iop(td, p->p_vmspace, va, buf, len, UIO_READ));
}
ssize_t
@@ -471,7 +557,7 @@ proc_writemem(struct thread *td, struct proc *p, vm_offset_t va, void *buf,
size_t len)
{
- return (proc_iop(td, p, va, buf, len, UIO_WRITE));
+ return (vmspace_iop(td, p->p_vmspace, va, buf, len, UIO_WRITE));
}
static int
@@ -1465,7 +1551,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
goto out;
}
PROC_UNLOCK(p);
- error = proc_rwmem(p, &uio);
+ error = proc_rwmem(p, &uio, 0);
piod->piod_len -= uio.uio_resid;
PROC_LOCK(p);
break;
diff --git a/sys/sys/imgact.h b/sys/sys/imgact.h
index c1c94a2eabfd..d35f85620ac9 100644
--- a/sys/sys/imgact.h
+++ b/sys/sys/imgact.h
@@ -122,6 +122,10 @@ int exec_shell_imgact(struct image_params *);
int exec_copyin_args(struct image_args *, const char *, char **, char **);
int pre_execve(struct thread *td, struct vmspace **oldvmspace);
void post_execve(struct thread *td, int error, struct vmspace *oldvmspace);
+bool execve_block(struct thread *td, struct proc *p);
+void execve_block_wait(struct thread *td, struct proc *p);
+void execve_unblock(struct thread *td, struct proc *p);
+void execve_block_pass(struct thread *td);
#endif
#endif /* !_SYS_IMGACT_H_ */
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 95a279742300..66b6a35c8d95 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -777,6 +777,7 @@ struct proc {
TAILQ_HEAD(, kq_timer_cb_data) p_kqtim_stop; /* (c) */
LIST_ENTRY(proc) p_jaillist; /* (d) Jail process linkage. */
+ u_int p_execblock; /* (c) Blockers for execve. */
};
#define p_session p_pgrp->pg_session
@@ -890,6 +891,8 @@ struct proc {
sync core registered */
#define P2_MEMBAR_GLOBE 0x00400000 /* membar global expedited
registered */
+#define P2_INEXEC_WAIT 0x80000000 /* Not same as in HEAD.
+ Waiters for P_INEXEC/p_execblock */
/* Flags protected by proctree_lock, kept in p_treeflags. */
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
index bb492f60a8ed..3e7c74dba815 100644
--- a/sys/sys/ptrace.h
+++ b/sys/sys/ptrace.h
@@ -249,7 +249,20 @@ int proc_write_fpregs(struct thread *_td, struct fpreg *_fpreg);
int proc_read_dbregs(struct thread *_td, struct dbreg *_dbreg);
int proc_write_dbregs(struct thread *_td, struct dbreg *_dbreg);
int proc_sstep(struct thread *_td);
-int proc_rwmem(struct proc *_p, struct uio *_uio);
+
+#define PRVM_BLOCK_EXEC 0x00000001
+#define PRVM_CHECK_VISIBILITY 0x00000002
+#define PRVM_CHECK_DEBUG 0x00000004
+
+#include <sys/_uio.h>
+struct vmspace;
+int proc_vmspace_ref(struct thread *_td, struct proc *_p, int _flags,
+ struct vmspace **_vmp);
+void proc_vmspace_unref(struct thread *_td, struct proc *_p, int _flags,
+ struct vmspace *_vm);
+ssize_t vmspace_iop(struct thread *td, struct vmspace *vm, vm_offset_t va,
+ void *buf, size_t len, enum uio_rw rw);
+int proc_rwmem(struct proc *_p, struct uio *_uio, int _flags);
ssize_t proc_readmem(struct thread *_td, struct proc *_p, vm_offset_t _va,
void *_buf, size_t _len);
ssize_t proc_writemem(struct thread *_td, struct proc *_p, vm_offset_t _va,