git: c495bc190985 - stable/14 - jail: Fix a race in prison_deref()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 26 Oct 2026 19:46:58 UTC
The branch stable/14 has been updated by markj:

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

commit c495bc19098528b642e93839e89ca29133025e81
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-09-25 15:16:24 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-26 14:12:15 +0000

    jail: Fix a race in prison_deref()
    
    If we're killing a jail which has some user refs pending, then we would
    first drop our ref and then kill all processes in the prison.  However,
    it's possible for the prison to be freed before we finish that
    operation, generally if the processes exit on their own before
    prison_proc_iterate() returns.
    
    Thus, defer the release of the prison refcount until after we've killed
    all procs.
    
    Reviewed by:    jamie
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D59984
    
    (cherry picked from commit 79af745f4c29ab3df037fcf0a4b4fb1ea393825f)
---
 sys/kern/kern_jail.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index 5e2992527cf2..73f4480c4184 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -3221,8 +3221,10 @@ prison_deref(struct prison *pr, int flags)
 			 * that need to be killed, either in this prison or its
 			 * descendants.
 			 */
-			if (refcount_load(&pr->pr_uref) > 0)
+			if (refcount_load(&pr->pr_uref) > 0) {
 				killpr = pr;
+				flags &= ~PD_DEREF;
+			}
 			/* Make sure the parent prison doesn't get killed. */
 			flags &= ~PD_KILL;
 		}
@@ -3282,8 +3284,10 @@ prison_deref(struct prison *pr, int flags)
 		sx_xunlock(&allprison_lock);
 
 	/* Kill any processes attached to a killed prison. */
-	if (killpr != NULL)
+	if (killpr != NULL) {
 		prison_proc_iterate(killpr, prison_kill_processes_cb, NULL);
+		prison_free(killpr);
+	}
 
 	/*
 	 * Finish removing any unreferenced prisons, which couldn't happen