git: 6772a8ece2c0 - releng/14.3 - execve_block(): a mechanism for mutual exclusion with execve() on the process
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 30 Jun 2026 17:20:28 UTC
The branch releng/14.3 has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=6772a8ece2c01362e37d25be00967f815b9c2b62
commit 6772a8ece2c01362e37d25be00967f815b9c2b62
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-05-26 17:36:20 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-06-30 02:32:01 +0000
execve_block(): a mechanism for mutual exclusion with execve() on the process
(cherry picked from commit e1a84b7708c2514769625c2af6c5034694013b6a)
(cherry picked from commit dc94d15a7136c4cfe3342c005d8a319f0583ed9a)
proc: Allow to make proc_rwmem() operate on a consistent address space
(cherry picked from commit e1b0d051bbf7e4e730470cbd6622f71a639834c3)
(cherry picked from commit e7578e37d832ecf1eee8cd3fed10df0e0cb1aa7e)
kern_proc.c: ensure stability of the vmspace we read the strings from
(cherry picked from commit a22c044cce41864a32af6b8281a3c416a2664d7f)
(cherry picked from commit 1f062247ef4e920e7fdd1ebe91b4600d69fe1e91)
kern_proc.c: ensure stability of the vmspace for sysctl kern.proc.vmmap
(cherry picked from commit 5fe6e08d2db56b530f0df2903ef24cfabab7b8ea)
(cherry picked from commit e856c10a4d6dfe30a730246b2050d9dd438085db)
kern_proc.c: ensure stability of the vmspace for sysctl kern.proc.vm_layout
(cherry picked from commit 31fa677e4af00b795bf671847b4dc72960c90f14)
(cherry picked from commit da1398dd6962b458fdcf25799fcb512f4d9c070d)
kern_proc.c: make kern.proc.osrel atomic
(cherry picked from commit 80626f34ee985671bb8c60ee986b89587b7a1511)
(cherry picked from commit b3f99ce653d456c9c829359b0dc83c4867f044b7)
kern_proc.c: disallow execve around sysctl kern.proc.kstacks
(cherry picked from commit 8b5abd9027b8b1f6290c756730ee3adebed007f4)
(cherry picked from commit 197f367cdda7919578d15b1562225d6db462c5ad)
kern_proc.c: disallow execve around sysctl kern.proc.rlimit
(cherry picked from commit 5a91fa5a7656c99e527fe7e6f6bf6bd9e85ab589)
(cherry picked from commit 35b95beb06cab0b10a7e3a697931851315fe3dc2)
kern_event: block the target process from execing for sysctl kern.proc.kqueue
(cherry picked from commit 79b384bd8fdff4869ce4799edb0342ff5c25b6fa)
(cherry picked from commit adb14791b752cf0f07dce44d840f01340aa6d6cb)
kern_resource.c: disallow execve around sysctl kern.proc.rlimitusage
(cherry picked from commit 836749817036b90b60af0584fa21f2d9dbd60ff7)
(cherry picked from commit 190d6677b11a1deed6bd0ef14fd4f990eea20f5b)
kern_procctl(PROC_WX_MAPPINGS_PERMIT): ensure stability of the target vmspace
(cherry picked from commit 6a572920f251ac8ac6a80a55d4d039736ea7dd65)
(cherry picked from commit 23ebf0c97ac5fcd6adb6cda8a8707b99257721a6)
linux_prlimit(): block execve for the target
(cherry picked from commit e41c28e67fac9cd22a85b160c5c9d0477ed03600)
(cherry picked from commit a11c4f036c1136ba90fbfda5d2025d18116562d8)
pfs_readdir(): block the target process from execing
(cherry picked from commit 4c0a0909f928baac982b7de4bfa5b5ca7de5291a)
(cherry picked from commit caa2e5cf7cfb07c115d143023ffb21546a60556a)
pseudofs: ensure that the target process vmspace is stable for VOP_READ/WRITE
(cherry picked from commit a4993bac41350e85bc9affb862d2974a1a09bb5e)
(cherry picked from commit ec0d7d01d6c45eff4043cf178addf65d1933d62a)
procfs, linprocfs: ensure target process vmspace stability
(cherry picked from commit 5db31f1b47b586a5091eb41fc957236f28b78dba)
(cherry picked from commit d7d5337291c983ba969e24c95df0bcab46b44621)
sys_set*id(9): wait for the execblocks to pass
(cherry picked from commit d0384f50dad25d3358d76f7556969ea1d515ba68)
(cherry picked from commit 34b95f53e7ac3a055b3d866d101a79e85d0022c4)
kern_exec.c: explicitly include sys/limits.h for UINT_MAX
(cherry picked from commit f5fce0bcc370a9700249e64901eabf2e0be4ecb0)
(cherry picked from commit bb1154f3ea20291b0c02821743084a6f1a7dd844)
Approved by: so
Security: FreeBSD-SA-26:39.execve
Security: CVE-2026-49415
---
sys/compat/linprocfs/linprocfs.c | 16 +---
sys/compat/linux/linux_misc.c | 15 +++
sys/fs/cuse/cuse.c | 4 +-
sys/fs/procfs/procfs_map.c | 17 ++--
sys/fs/procfs/procfs_mem.c | 6 +-
sys/fs/pseudofs/pseudofs_vnops.c | 69 ++++++++++----
sys/kern/kern_event.c | 22 ++++-
sys/kern/kern_exec.c | 81 ++++++++++++++++-
sys/kern/kern_exit.c | 1 +
sys/kern/kern_fork.c | 1 +
sys/kern/kern_proc.c | 191 +++++++++++++++++++++++----------------
sys/kern/kern_procctl.c | 23 +++--
sys/kern/kern_prot.c | 20 +++-
sys/kern/kern_resource.c | 27 ++++--
sys/kern/sys_process.c | 124 +++++++++++++++++++++----
sys/sys/imgact.h | 4 +
sys/sys/proc.h | 3 +
sys/sys/ptrace.h | 15 ++-
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 b87c05322e16..41977e5a899c 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -2007,6 +2007,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)) {
@@ -2034,6 +2035,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;
@@ -2046,6 +2048,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);
@@ -2068,6 +2078,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 0bdfedffafcb..0695c2f727ea 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;
@@ -884,11 +903,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);
@@ -956,6 +977,7 @@ pfs_readdir(struct vop_readdir_args *va)
resid -= PFS_DELEN;
}
if (proc != NULL) {
+ execve_unblock(td, proc);
_PRELE(proc);
PROC_UNLOCK(proc);
}
@@ -1080,6 +1102,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;
@@ -1099,36 +1122,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 eb77a5064113..089548dad97d 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>
@@ -3028,10 +3029,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 FREEBSD_COMPAT32
compat32 = SV_CURPROC_FLAG(SV_ILP32);
@@ -3039,6 +3036,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;
@@ -3059,7 +3067,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 484adaac91ec..b6b599ffd281 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>
@@ -373,6 +373,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.
@@ -428,6 +499,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);
@@ -896,7 +968,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;
@@ -978,7 +1054,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 0c3070f2e360..5bcd4219bf91 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 ee58ad42bce6..96ed239fafb0 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 b7fa7bfb60e9..d8a57ac46673 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
*** 695 LINES SKIPPED ***