From e2a37cba3cd9c009c8e03194b8afb0e48d79a7d0 Mon Sep 17 00:00:00 2001 From: Ollie Agent Date: Sun, 2 Aug 2026 10:29:37 +0200 Subject: [PATCH] fs/session: fix dangling pointer in eventRing cond initialization The closure returned eventRing by value, copying the struct. e.cond.L was set to &e.mu (the local variable's mutex), but after the copy evRing.cond.L still pointed to the ORIGINAL e.mu while evRing.mu was a different mutex. This caused sync.Cond.Wait to unlock the wrong (already-unlocked) mutex, panicking every consumer connecting to /eventwait. Fixed by using *sync.Cond with sync.NewCond, making evRing a pointer to prevent any copying of the struct with its cond. --- fs/session/root.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/session/root.go b/fs/session/root.go index f530e4b..f4ed3ae 100644 --- a/fs/session/root.go +++ b/fs/session/root.go @@ -23,7 +23,7 @@ const eventRingCap = 100 type eventRing struct { mu sync.Mutex - cond sync.Cond + cond *sync.Cond buf []string // circular: buf[start%cap]..buf[(start+count-1)%cap] cap int start int @@ -31,9 +31,9 @@ type eventRing struct { total int64 // monotonic counter; doubles as stable offset } -var evRing = func() eventRing { - e := eventRing{cap: eventRingCap, buf: make([]string, eventRingCap)} - e.cond.L = &e.mu +var evRing = func() *eventRing { + e := &eventRing{cap: eventRingCap, buf: make([]string, eventRingCap)} + e.cond = sync.NewCond(&e.mu) return e }()