From 4176d8348fa2b1ad073737eb3dea5f3ee11ab2f1 Mon Sep 17 00:00:00 2001 From: bneradt Date: Mon, 10 Aug 2026 13:33:15 -0500 Subject: [PATCH] Fix intermittent cache unit test segfault in Fedora CI The Fedora CI job fails every so often with a SIGSEGV in one of the cache unit tests, always with the same stack: strrchr() called from SourceLocation::str(), from lock_waiting(), from Mutex_trylock(). In DEBUG builds a thread that fails to acquire a mutex reports the holder's srcloc and handler, but those fields belong to whichever thread holds the mutex, and a waiter that just failed to acquire it holds nothing. That read races with the holder publishing the fields on acquire and clearing them in Mutex_unlock(). Because SourceLocation::str() loads file once for valid() and again for strrchr(), and because the clear happens before the mutex is released, a waiter can pass the validity check and then dereference a null file. The cache unit tests are the only ones that enable the locks debug tag, so they are the only ones that reach this code at all. This patch addresses this by reporting the waiting site rather than the holder's. That SourceLocation is a MakeSourceLocation() temporary owned by the caller, so nothing else can mutate it, and a holder snapshot is stale the instant it is taken in any case. The holder is still reported by lock_holding(), which runs from Mutex_unlock() where the caller owns those fields. Fixes: #13524 Co-Authored-By: Claude Opus 5 --- include/iocore/eventsystem/Lock.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/iocore/eventsystem/Lock.h b/include/iocore/eventsystem/Lock.h index f41c862ba71..2fcef5fdadd 100644 --- a/include/iocore/eventsystem/Lock.h +++ b/include/iocore/eventsystem/Lock.h @@ -261,7 +261,13 @@ Mutex_trylock( if (m->thread_holding != t) { if (!ink_mutex_try_acquire(&m->the_mutex)) { #ifdef DEBUG - lock_waiting(m->srcloc, m->handler); + // Report the waiting site, not the holder's. m->srcloc and m->handler + // belong to whichever thread holds the mutex, and this thread just failed + // to acquire it, so reading them races with the holder publishing them + // below and clearing them in Mutex_unlock(). Any holder snapshot is stale + // the instant it is taken anyway; lock_holding() still reports the holder + // from Mutex_unlock(), where the fields are owned by the caller. + lock_waiting(location, ahandler); #ifdef LOCK_CONTENTION_PROFILING m->unsuccessful_nonblocking_acquires++; m->nonblocking_acquires++;