git: 46f7b5a64048 - releng/15.1 - 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: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 ***