From nobody Tue Jun 30 17:20:28 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gqVK92R9Yz6jYCr for ; Tue, 30 Jun 2026 17:20:29 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gqVK903Xnz3qHC for ; Tue, 30 Jun 2026 17:20:29 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782840029; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=mMK4aX6vwFf9Bdz3X+ii6VLlxxyTepV4GlpoJAtBHNA=; b=Mh1dWWHa+SYZQxIU+5ppTVOd8hJroXmNRXA4yJdpnlx0pw8X4X4a6ININrhwAX0NvSSYIL oaaVfWiYaSjpSx/+q5lVbua1uVUYNQp8l3rEhpSc7KaLnAYc6a5phl4nro9R1I0deIDuK7 R1P41ZNuPq7f7pB24M6eSGmLt0zgxZ5woX0p4pAsCUNgenVtUi+uSGnjQqCeJF9NVYi/oe oZkzOKMy8MEWvThMOJOoHRRmE5px6eEMF5DL3kiZlbeGREq9LC41qKg89Ct9HBW/yemdk/ CYa7JAjqSaJnMTjYCNnKqazqGzM75Pd+0jWA6+Vxqqi5j7TflgevD6VrHM6Qzw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782840029; a=rsa-sha256; cv=none; b=jcVoiUc6ZGDQDTRjTFccExzoQ11cH5V95EYBlKDCZpdo5roAhaM2kv62kjj6c6e7up0+mU 3C2uNyqGo27e44zFMlkw7PwPWTNYAVOgI1j2mrWhofbTT6ecqOqIyKonjpwd08T7EjVwCH 7oroTceVOr5jkxuMtjniXKkcYtOAl7A/tVMzXOKSV+7djM0JVZckqqLgY3faw1nYpVAU7+ P0B0VLzjdgOOZ7zFKaGpfpGzkGEW37MWLSMRvILIt4JGRpX42+b3Ef42Ije8cgVhx15Hvo WZlwamxkf666ShWAlJs26+p1bJD9SZqiOwIXzvl80fRiADEUQCncLeaLpvoBWw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782840029; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=mMK4aX6vwFf9Bdz3X+ii6VLlxxyTepV4GlpoJAtBHNA=; b=yHKyWtK1RvZNlqV2b8zfZ8Dd1w1zHfI/rLEX8NsJ0m0HoN/EbDYC7E3IB8uTW8V8GHYTAt kAYthduNSWE/JC4lps9b7dg0yPx0ba3T2PAy/JuE7hC0vSCz7LUv2h5sa6Gwn6zJuq69St QlLLhy0sld0To0kjeZxOswZWJtAvmXW5RMI85SxZeaPy1DgW6n/Px1G7wtZFjpW/FkraGH PqnPnd86zf7K/CT73Hg7Ga7/BtGnXW6rd7YcE8B2RguDx2cpRWHncMkhXUF6Me/ydt6rIT XD8OJDllxYtL3ja13YbbVz1A0qaTUMRzW7rGFa3qX12Ul3qPlZ2QEd90PnYxGA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gqVK8644hztDM for ; Tue, 30 Jun 2026 17:20:28 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 45e07 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 30 Jun 2026 17:20:28 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: Konstantin Belousov From: Mark Johnston Subject: git: 6772a8ece2c0 - releng/14.3 - execve_block(): a mechanism for mutual exclusion with execve() on the process List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/releng/14.3 X-Git-Reftype: branch X-Git-Commit: 6772a8ece2c01362e37d25be00967f815b9c2b62 Auto-Submitted: auto-generated Date: Tue, 30 Jun 2026 17:20:28 +0000 Message-Id: <6a43fadc.45e07.2dd98d12@gitrepo.freebsd.org> The branch releng/14.3 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=6772a8ece2c01362e37d25be00967f815b9c2b62 commit 6772a8ece2c01362e37d25be00967f815b9c2b62 Author: Konstantin Belousov AuthorDate: 2026-05-26 17:36:20 +0000 Commit: Mark Johnston 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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 #include "opt_capsicum.h" #include "opt_hwpmc_hooks.h" #include "opt_ktrace.h" @@ -45,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 ***