From e78d9248a39cdd1fb6d9b2b916721b257e883930 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Wed, 12 Aug 2026 22:42:09 -0700 Subject: [PATCH 1/2] Guard the shutdown log wakeup against an unallocated notify array Log::init() sets Log::preproc_threads to 1 immediately, but Log::preproc_notify is not allocated until Log::create_threads(). A shutdown signal arriving between those two points reaches AutoStopCont::mainEvent, which walks preproc_threads entries of a null array and crashes with a SIGSEGV at address zero. A previous change added this same guard to the two call sites in LogObject.cc but did not cover the copy in traffic_server.cc, which was introduced separately. Production cores show the unguarded site still firing on builds that already carry the LogObject.cc guards. --- src/traffic_server/traffic_server.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/traffic_server/traffic_server.cc b/src/traffic_server/traffic_server.cc index 1f174fb1810..5e6f55a7880 100644 --- a/src/traffic_server/traffic_server.cc +++ b/src/traffic_server/traffic_server.cc @@ -309,9 +309,12 @@ struct AutoStopCont : public Continuation { CacheShm::mark_clean_shutdown(); } - // Wake preproc threads to drain remaining log buffers before exit. - for (int i = 0; i < Log::preproc_threads; i++) { - Log::preproc_notify[i].signal(); + // Wake preproc threads to drain remaining log buffers before exit. Log::preproc_threads is set by Log::init(), but the + // notify array is not allocated until Log::create_threads(), so a shutdown arriving between the two finds it null. + if (Log::preproc_notify != nullptr) { + for (int i = 0; i < Log::preproc_threads; i++) { + Log::preproc_notify[i].signal(); + } } delete this; return EVENT_CONT; From 6f9f2b11482e6bfd3bdb4d01fd5b1932c86a314c Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Thu, 13 Aug 2026 10:04:28 -0700 Subject: [PATCH 2/2] Widen the comment to cover the permanent null case The array is also null for the whole life of the log-only tools, where Log::load_config returns without ever creating the log threads. Saying only that a shutdown can arrive mid startup invites a later reader to prove that race unreachable and drop a guard that is still load bearing. --- src/traffic_server/traffic_server.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/traffic_server/traffic_server.cc b/src/traffic_server/traffic_server.cc index 5e6f55a7880..ac9029594e8 100644 --- a/src/traffic_server/traffic_server.cc +++ b/src/traffic_server/traffic_server.cc @@ -309,8 +309,9 @@ struct AutoStopCont : public Continuation { CacheShm::mark_clean_shutdown(); } - // Wake preproc threads to drain remaining log buffers before exit. Log::preproc_threads is set by Log::init(), but the - // notify array is not allocated until Log::create_threads(), so a shutdown arriving between the two finds it null. + // Wake preproc threads to drain remaining log buffers before exit. The notify array is allocated only once the log + // threads are spawned, which has not happened yet this early in startup and never happens at all for the log-only + // tools, so a null array means there is no preproc thread in existence and nothing to wake. if (Log::preproc_notify != nullptr) { for (int i = 0; i < Log::preproc_threads; i++) { Log::preproc_notify[i].signal();