git: 2893d756a70f - stable/15 - jail: Fix a race in prison_deref()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Thu, 22 Oct 2026 17:32:05 UTC
The branch stable/15 has been updated by markj:

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

commit 2893d756a70f58cdbb7f066cfb873501d22d57c6
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-09-25 15:16:24 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-22 15:45:41 +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 5629f529b1dd..522b2306c928 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -3585,8 +3585,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;
 		}
@@ -3646,8 +3648,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