git: dc761982793e - stable/14 - epoch: Fix use-after-free in epoch_trace_report()

From: R. Christian McDonald <rcm_at_FreeBSD.org>
Date: Wed, 28 Oct 2026 20:41:26 UTC
The branch stable/14 has been updated by rcm:

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

commit dc761982793e0e4eb360a205f3e73eacc00fd185
Author:     R. Christian McDonald <rcm@FreeBSD.org>
AuthorDate: 2026-09-30 13:41:53 +0000
Commit:     R. Christian McDonald <rcm@FreeBSD.org>
CommitDate: 2026-10-28 20:38:03 +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
    
    (cherry picked from commit dd5dc8f6e51c5132f2dc885f5c1b0492cdf3052c)
---
 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 84ccce9f77b2..24147629186a 100644
--- a/sys/kern/subr_epoch.c
+++ b/sys/kern/subr_epoch.c
@@ -194,6 +194,7 @@ epoch_trace_report(const char *fmt, ...)
 {
 	va_list ap;
 	struct stackentry se, *new;
+	bool dup;
 
 	stack_save(&se.se_stack);
 
@@ -206,10 +207,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);