git: dd5dc8f6e51c - main - epoch: Fix use-after-free in epoch_trace_report()

From: R. Christian McDonald <rcm_at_FreeBSD.org>
Date: Wed, 30 Sep 2026 13:51:27 UTC
The branch main has been updated by rcm:

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

commit dd5dc8f6e51c5132f2dc885f5c1b0492cdf3052c
Author:     R. Christian McDonald <rcm@FreeBSD.org>
AuthorDate: 2026-09-30 13:41:53 +0000
Commit:     R. Christian McDonald <rcm@FreeBSD.org>
CommitDate: 2026-09-30 13:51:18 +0000

    epoch: Fix use-after-free in epoch_trace_report()
    
    epoch_trace_report() assigned the return value of RB_INSERT() back to
    the new element. When two threads report the same stack concurrently,
    the loser's RB_INSERT() returns the element already in the tree, and
    that element was freed while still linked, leaking the new allocation.
    The next lookup touches freed memory; KASAN catches it as a
    use-after-free.
    
    Keep the return value separate and free the new element instead. The
    thread that won the race prints the report, so return without printing
    it a second time.
    
    Reviewed by:            markj
    Fixes:                  173c062a569b ("Improve EPOCH_TRACE")
    MFC after:              1 week
    Sponsored by:           Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D60162
---
 sys/kern/subr_epoch.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/sys/kern/subr_epoch.c b/sys/kern/subr_epoch.c
index 266230b04b43..e81b91295d48 100644
--- a/sys/kern/subr_epoch.c
+++ b/sys/kern/subr_epoch.c
@@ -193,6 +193,7 @@ epoch_trace_report(const char *fmt, ...)
 {
 	va_list ap;
 	struct stackentry se, *new;
+	bool dup;
 
 	stack_save(&se.se_stack);
 
@@ -205,10 +206,13 @@ epoch_trace_report(const char *fmt, ...)
 		bcopy(&se.se_stack, &new->se_stack, sizeof(struct stack));
 
 		mtx_lock(&epoch_stacks_lock);
-		new = RB_INSERT(stacktree, &epoch_stacks, new);
+		dup = RB_INSERT(stacktree, &epoch_stacks, new) != NULL;
 		mtx_unlock(&epoch_stacks_lock);
-		if (new != NULL)
+		if (dup) {
+			/* Lost a race; the other thread reports it. */
 			free(new, M_STACK);
+			return;
+		}
 	}
 
 	va_start(ap, fmt);