From nobody Sun Jul 5 05:13:48 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 4gtFyP19Zpz6jnjJ for ; Sun, 05 Jul 2026 05:13:49 +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 4gtFyN57v4z3Ldn for ; Sun, 05 Jul 2026 05:13:48 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1783228428; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=dHY08cogkfn8IxmXn8OVwwNNruVlUB5+IVrJKhRcNyY=; b=sqpFqSeW6Qmiwe4isWCGD5n0veIuFLqgh+HAdAPf5KEYShSuW5u45TTI6vfUxIxA9xFA0g 55PQt9Av4KmFkXepoUjg2CjH9FXNXiqlB3IyxzIl7aa9K6njmfWkNy+TLvqMM9Va9qwXdD p6qwBv6BTukuHXvuPGWSd1dCryTnSGYQ/ZLG6VxFPWysPGvcIrd08VRQzaXQ6Gpii1r6qq C1dZHJT82U/RF2LgwF42DYFxwlCHpvDpQ5TYmLxe/wnz4KG9rCFoiIuu0aBuTLFbP9rWh7 JZTwpVes9WvpKyJjVvQl++06dQm0AMAV5dLmLiUMyKZiEP8ci4uQeVl094HKqA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1783228428; a=rsa-sha256; cv=none; b=mLq6i5oKSeprzJflwC641ZPZDmzD3ztDtawqFXSRJ/GxQTR5tVrci8XEf0wQdrVb3ioejl iA3biVnC5Aar+7EoEM1h97DZSbIVSJN/muj4QiaIJKWs5D7V+CNCiSfXznyuaveJCDrnag 3b7QFUpNlifX9DYccOeoypy6iOUZQiauNjNWLIeNuJc5XPwLbz8JKoGvOmNG/nWT5YM4x4 V7NxFZIgDTbLUcbAkvv7S2x0h1YSxbmwB8CbrycwXwg9yzuUCus0bEQN7aoLydGKS1ki5d D07342iTNPDcpREiZr2uKotV9a3DTybHCoiQAIfDWo3LCsdlO3s9YX4za4/OYg== 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=1783228428; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=dHY08cogkfn8IxmXn8OVwwNNruVlUB5+IVrJKhRcNyY=; b=PHawLlZGUwU0KPn4S0l72FhIPCZuMNnbqQLiaiolF2NdV14Cn4XPs8846ynfrRdR5cDw4K KT7G3Wox++t9qiUO/YIw0+Oo/5PbKlG7PveGe7EyPo4RqL45xgE6YDamEbfCFdOMm7pYzG VhuNfBmNv9/XCVXX6wt0n0q06VF7d3e1h1CyrxHTfcKKYza01KeJNCLrDraSQ47s3Xxdeh 9hvBG3yaHiSpDz0xyeCeHVxhVhzYtUllNNKP54w0K9V9OflFmmAl1vX4QSMpJTg6qfYuc3 egUlIpLK79ssaEiWB0bB6F8MXfvNmMCW62TgQSM8p/ZxXN3NrRvNKM3V2jKOlg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gtFyN3vVFz13dl for ; Sun, 05 Jul 2026 05:13:48 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 22848 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 05 Jul 2026 05:13:48 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Jamie Gritton Subject: git: 5ba2b4f773c2 - stable/14 - jail: prevent a race between jail_attach in different threads 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: jamie X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: 5ba2b4f773c27f139ce840ca93b7c5dad83d1984 Auto-Submitted: auto-generated Date: Sun, 05 Jul 2026 05:13:48 +0000 Message-Id: <6a49e80c.22848.48d188d3@gitrepo.freebsd.org> The branch stable/14 has been updated by jamie: URL: https://cgit.FreeBSD.org/src/commit/?id=5ba2b4f773c27f139ce840ca93b7c5dad83d1984 commit 5ba2b4f773c27f139ce840ca93b7c5dad83d1984 Author: Jamie Gritton AuthorDate: 2026-07-02 22:48:07 +0000 Commit: Jamie Gritton CommitDate: 2026-07-05 05:13:27 +0000 jail: prevent a race between jail_attach in different threads Attaching to a jail changes its root directory and its process credentials. These operations both require unlocking the jail, and also need allprison_lock unlocked. That means that if two threads are trying to attach to different jails at the same time, it's possible for the process to end up with one jail's root directory but the other jail's credentials. Solve this by forcing the process into single-threaded mode during system calls that attach to a jail (jail_attach, jail_attach_jd, and sometimes jail_set). Reviewed by: kib, markj Differential Revision: https://reviews.freebsd.org/D57858 (cherry picked from commit d4e0f4dab2d7f4de46bb79db1ca7e6e8a2e34746) --- sys/kern/kern_jail.c | 87 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index f40e7c4e176c..5063410e63b1 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -150,6 +150,8 @@ int lastprid = 0; static int get_next_prid(struct prison **insprp); static int do_jail_attach(struct thread *td, struct prison *pr, int *drflagsp); +static int prison_attach_thread_single(struct thread *td); +static void prison_attach_thread_single_end(struct thread *td); static void prison_complete(void *context, int pending); static void prison_deref(struct prison *pr, int flags); static void prison_deref_kill(struct prison *pr, struct prisonlist *freeprison); @@ -1032,6 +1034,20 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) if (flags & ~JAIL_SET_MASK) return (EINVAL); + /* Only let a single thread in the process try to attach at a time. */ + if (flags & JAIL_ATTACH) { + error = prison_attach_thread_single(td); + if (error != 0) + return (error); + } + +#ifdef INET + ip4 = NULL; +#endif +#ifdef INET6 + ip6 = NULL; +#endif + g_path = NULL; /* * Check all the parameters before committing to anything. Not all * errors can be caught early, but we may as well try. Also, this @@ -1043,15 +1059,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) * than duplicate it under a different name. */ error = vfs_buildopts(optuio, &opts); - if (error) - return (error); -#ifdef INET - ip4 = NULL; -#endif -#ifdef INET6 - ip6 = NULL; -#endif - g_path = NULL; + if (error) { + opts = NULL; + goto done_free; + } cuflags = flags & (JAIL_CREATE | JAIL_UPDATE); if (!cuflags) { @@ -2203,7 +2214,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) #endif if (g_path != NULL) free(g_path, M_TEMP); - vfs_freeopts(opts); + if (opts != NULL) + vfs_freeopts(opts); + if (flags & JAIL_ATTACH) + prison_attach_thread_single_end(td); return (error); } @@ -2637,6 +2651,10 @@ sys_jail_attach(struct thread *td, struct jail_attach_args *uap) error = priv_check(td, PRIV_JAIL_ATTACH); if (error) return (error); + /* Only let a single thread in the process try to attach at a time. */ + error = prison_attach_thread_single(td); + if (error != 0) + return (error); sx_slock(&allprison_lock); drflags = PD_LIST_SLOCKED; @@ -2657,6 +2675,7 @@ sys_jail_attach(struct thread *td, struct jail_attach_args *uap) done: prison_deref(pr, drflags); + prison_attach_thread_single_end(td); return (error); } @@ -2674,15 +2693,6 @@ do_jail_attach(struct thread *td, struct prison *pr, int *drflagsp) sx_assert(&allprison_lock, SX_LOCKED); KASSERT(prison_isvalid(pr), ("Attaching to invalid prison %p", pr)); - /* - * XXX: Note that there is a slight race here if two threads - * in the same privileged process attempt to attach to two - * different jails at the same time. It is important for - * user processes not to do this, or they might end up with - * a process root from one prison, but attached to the jail - * of another. - */ - /* * Note the caller's locking state, but gain and track our own * references. The caller will see that locks have been @@ -2764,7 +2774,7 @@ do_jail_attach(struct thread *td, struct prison *pr, int *drflagsp) e_unlock: VOP_UNLOCK(pr->pr_root); e_revert_osd: - /* Tell modules this thread is still in its old jail after all. */ + /* Tell modules this process is still in its old jail after all. */ if (!(drflags & (PD_LIST_SLOCKED | PD_LIST_XLOCKED))) { sx_slock(&allprison_lock); drflags |= PD_LIST_SLOCKED; @@ -2774,6 +2784,43 @@ do_jail_attach(struct thread *td, struct prison *pr, int *drflagsp) return (error); } +/* + * Only one thread in a process should try to attach to a jail, or + * they might end up with a process root from one prison, but attached + * to the jail of another. Enforce this by making the process run in + * single-threaded mode for the duration of the system call, which + * also prevents other related calls such as chroot. + */ +static int +prison_attach_thread_single(struct thread *td) +{ + struct proc *p; + int error; + + error = 0; + p = td->td_proc; + if ((atomic_load_int(&p->p_flag) & P_HADTHREADS) != 0) { + PROC_LOCK(p); + if (thread_single(p, SINGLE_BOUNDARY)) + error = ERESTART; + PROC_UNLOCK(p); + } + return (error); +} + +static void +prison_attach_thread_single_end(struct thread *td) +{ + struct proc *p; + + p = td->td_proc; + if ((atomic_load_int(&p->p_flag) & P_HADTHREADS) != 0) { + PROC_LOCK(p); + thread_single_end(p, SINGLE_BOUNDARY); + PROC_UNLOCK(p); + } +} + /* * Returns a locked prison instance, or NULL on failure. */