git: 46f7b5a64048 - releng/15.1 - execve_block(): a mechanism for mutual exclusion with execve() on the process

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Tue, 30 Jun 2026 17:21:55 UTC
The branch releng/15.1 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=46f7b5a64048b17d87badc989788cc041ec9f5a3

commit 46f7b5a64048b17d87badc989788cc041ec9f5a3
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-05-26 17:36:20 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-06-29 19:16:40 +0000

    execve_block(): a mechanism for mutual exclusion with execve() on the process
    
    (cherry picked from commit e1a84b7708c2514769625c2af6c5034694013b6a)
    (cherry picked from commit a80e40ce9ee0a0641927730a193ff1f5641e71b6)
    
    execve_block_pass(9): a helper to wait for the execblock to pass
    
    (cherry picked from commit 450fe67d19eb2ea8e27109214ff21064fdcf808c)
    (cherry picked from commit cbd58477285888b1b7ff21bdd0bc039f7ffad4ef)
    
    proc: Allow to make proc_rwmem() operate on a consistent address space
    
    (cherry picked from commit e1b0d051bbf7e4e730470cbd6622f71a639834c3)
    (cherry picked from commit be6e5131066753dc23f8e9f223acc023b7618b63)
    
    kern_proc.c: ensure stability of the vmspace we read the strings from
    
    (cherry picked from commit a22c044cce41864a32af6b8281a3c416a2664d7f)
    (cherry picked from commit 3748c2e257046056b969a5d47231695798198f4d)
    
    kern_proc.c: ensure stability of the vmspace for sysctl kern.proc.vmmap
    
    (cherry picked from commit 5fe6e08d2db56b530f0df2903ef24cfabab7b8ea)
    (cherry picked from commit 20ed389b365030b868298d7ab4e8a01270c06c9f)
    
    kern_proc.c: ensure stability of the vmspace for sysctl kern.proc.vm_layout
    
    (cherry picked from commit 31fa677e4af00b795bf671847b4dc72960c90f14)
    (cherry picked from commit eb03358c407d6f3c073320bdc26a5e4ceb923ca1)
    
    kern_proc.c: make kern.proc.osrel atomic
    
    (cherry picked from commit 80626f34ee985671bb8c60ee986b89587b7a1511)
    (cherry picked from commit 5e633db3f8e126578c097c9649ba3be552664c6a)
    
    kern_proc.c: disallow execve around sysctl kern.proc.kstacks
    
    (cherry picked from commit 8b5abd9027b8b1f6290c756730ee3adebed007f4)
    (cherry picked from commit 60d8df61846fc1f5f7e2a400d2b0237c9f1a6be8)
    
    kern_proc.c: disallow execve around sysctl kern.proc.rlimit
    
    (cherry picked from commit 5a91fa5a7656c99e527fe7e6f6bf6bd9e85ab589)
    (cherry picked from commit 24d8b3364405e5c32618c6930d4fdd8bc543a08f)
    
    kern_event: block the target process from execing for sysctl kern.proc.kqueue
    
    (cherry picked from commit 79b384bd8fdff4869ce4799edb0342ff5c25b6fa)
    (cherry picked from commit d172933cf69fb4dc58e07261653481a33cdf1da0)
    
    kern_resource.c: disallow execve around sysctl kern.proc.rlimitusage
    
    (cherry picked from commit 836749817036b90b60af0584fa21f2d9dbd60ff7)
    (cherry picked from commit 035f66ff2924808f534df9065388aabf66e77266)
    
    kern_procctl(PROC_WX_MAPPINGS_PERMIT): ensure stability of the target vmspace
    
    (cherry picked from commit 6a572920f251ac8ac6a80a55d4d039736ea7dd65)
    (cherry picked from commit d80a7fd6e6df65b4d85e6977557c3fbe8d9ff02e)
    
    linux_prlimit(): block execve for the target
    
    (cherry picked from commit e41c28e67fac9cd22a85b160c5c9d0477ed03600)
    (cherry picked from commit e9002239571fb1502505ff85061934ef0ebd2e1e)
    
    pfs_readdir(): block the target process from execing
    
    (cherry picked from commit 4c0a0909f928baac982b7de4bfa5b5ca7de5291a)
    (cherry picked from commit 1744ce4b052d4aea94c1c3b87fefabd0952e6082)
    
    pseudofs: ensure that the target process vmspace is stable for VOP_READ/WRITE
    
    (cherry picked from commit a4993bac41350e85bc9affb862d2974a1a09bb5e)
    (cherry picked from commit 277f612073d0b086604448814867cadaacad4ca8)
    
    procfs, linprocfs: ensure target process vmspace stability
    
    (cherry picked from commit 5db31f1b47b586a5091eb41fc957236f28b78dba)
    (cherry picked from commit 78981848b85fdd826eb830dfdd6d92c14b0f8bb3)
    
    sys_set*id(9): wait for the execblocks to pass
    
    (cherry picked from commit d0384f50dad25d3358d76f7556969ea1d515ba68)
    (cherry picked from commit e0492444761b638ff072222d39ba5d6088a63370)
    
    kern_exec.c: explicitly include sys/limits.h for UINT_MAX
    
    (cherry picked from commit f5fce0bcc370a9700249e64901eabf2e0be4ecb0)
    (cherry picked from commit 42d8b0bf7a5a074c7035585a06dd44aed1b2cbdd)
    
    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             |  83 ++++++++++++++++-
 sys/kern/kern_exit.c             |   1 +
 sys/kern/kern_fork.c             |   1 +
 sys/kern/kern_proc.c             | 191 +++++++++++++++++++++++----------------
 sys/kern/kern_procctl.c          |  21 +++--
 sys/kern/kern_prot.c             |  20 +++-
 sys/kern/kern_resource.c         |  27 ++++--
 sys/kern/sys_process.c           | 131 ++++++++++++++++++++++-----
 sys/sys/imgact.h                 |   4 +
 sys/sys/proc.h                   |   3 +-
 sys/sys/ptrace.h                 |  15 ++-
 18 files changed, 470 insertions(+), 176 deletions(-)

diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c
index 7ac48786c77b..df3434848a81 100644
--- a/sys/compat/linprocfs/linprocfs.c
+++ b/sys/compat/linprocfs/linprocfs.c
@@ -1315,19 +1315,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;
@@ -1425,7 +1419,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 937b010c8435..d35429e17f12 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -2010,6 +2010,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(td, args->resource, &rlim)) {
@@ -2037,6 +2038,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;
@@ -2049,6 +2051,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);
@@ -2071,6 +2081,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 b914b2d5017c..8f67c4b5572b 100644
--- a/sys/fs/cuse/cuse.c
+++ b/sys/fs/cuse/cuse.c
@@ -916,7 +916,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) {
@@ -935,7 +935,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 d4913a7b0f1d..1ed785f1ad90 100644
--- a/sys/fs/procfs/procfs_map.c
+++ b/sys/fs/procfs/procfs_map.c
@@ -40,6 +40,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>
@@ -93,15 +94,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)) {
@@ -111,9 +111,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) {
@@ -238,6 +235,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 0020b8f8a8d8..eb692c32923b 100644
--- a/sys/fs/procfs/procfs_mem.c
+++ b/sys/fs/procfs/procfs_mem.c
@@ -60,11 +60,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 8c7a0949f024..01bd22fbefd1 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -49,6 +49,7 @@
 #include <sys/filedesc.h>
 #include <sys/filio.h>
 #include <sys/fcntl.h>
+#include <sys/imgact.h>
 #include <sys/jail.h>
 #include <sys/jaildesc.h>
 #include <sys/kthread.h>
@@ -3381,10 +3382,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);
@@ -3392,6 +3389,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;
@@ -3412,7 +3420,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 0a9ae0aabb3e..c75354a4ffa6 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_hwt_hooks.h"
@@ -46,6 +45,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>
@@ -385,6 +385,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_flag |= P_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_flag & P_INEXEC_WAIT) != 0) {
+		p->p_flag &= ~P_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_flag |= P_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.
@@ -440,6 +511,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);
 
@@ -909,7 +981,10 @@ interpret:
 	 * as we're now a bona fide freshly-execed process.
 	 */
 	KNOTE_LOCKED(p->p_klist, NOTE_EXEC);
-	p->p_flag &= ~P_INEXEC;
+	MPASS(p->p_execblock == 0);
+	if ((p->p_flag & P_INEXEC_WAIT) != 0)
+		wakeup(&p->p_execblock);
+	p->p_flag &= ~(P_INEXEC | P_INEXEC_WAIT);
 
 	/* clear "fork but no exec" flag, as we _are_ execing */
 	p->p_acflag &= ~AFORK;
@@ -1005,7 +1080,9 @@ exec_fail_dealloc:
 exec_fail:
 		/* we're done here, clear P_INEXEC */
 		PROC_LOCK(p);
-		p->p_flag &= ~P_INEXEC;
+		if ((p->p_flag & P_INEXEC_WAIT) != 0)
+			wakeup(&p->p_execblock);
+		p->p_flag &= ~(P_INEXEC | P_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 18ea3a7bd29d..667545bd6ac7 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -323,6 +323,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 a453af959bb1..07385752c02d 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -430,6 +430,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 758f54c540ab..3f0bb5a915f4 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -43,6 +43,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>
@@ -1838,8 +1839,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;
 
@@ -1848,7 +1849,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);
@@ -1864,8 +1865,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;
@@ -1876,8 +1877,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:
@@ -1900,8 +1901,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;
@@ -1917,7 +1918,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;
 	}
@@ -1938,8 +1939,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;
@@ -1949,11 +1950,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:
@@ -1991,8 +1994,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;
@@ -2014,7 +2017,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);
 	}
@@ -2030,6 +2033,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;
@@ -2042,9 +2046,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
@@ -2054,7 +2063,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;
@@ -2071,6 +2080,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);
 }
 
@@ -2091,11 +2102,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)
@@ -2408,6 +2425,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;
 
@@ -2416,11 +2434,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);
 	}
@@ -2532,7 +2551,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);
@@ -2627,6 +2646,7 @@ kern_proc_vmmap_out(struct proc *p, struct sbuf *sb, ssize_t maxlen, int flags)
 	struct ucred *cred;
 	struct vnode *vp;
 	struct vmspace *vm;
+	struct thread *td;
 	vm_offset_t addr;
 	unsigned int last_timestamp;
 	int error;
@@ -2638,10 +2658,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);
 
@@ -2745,7 +2766,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;
@@ -2801,7 +2822,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);
@@ -2840,7 +2861,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;
@@ -2851,7 +2872,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);
 
@@ -2860,6 +2882,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);
@@ -2872,15 +2902,6 @@ sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS)
 		PROC_LOCK(p);
 	} while (numthreads < p->p_numthreads);
 
-	/*
*** 716 LINES SKIPPED ***