This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 90b623ebb75d485c0088e65c1db6bc2458e3cd09 Author: Brian Neradt <[email protected]> AuthorDate: Tue May 12 11:07:09 2026 -0500 Fix bg fill teardown crash in update_size_and_time_stats (#13114) Production crash logs show ink_assert(0) firing in HttpTransact::update_size_and_time_stats when an HTTP/2 stream close re-enters HttpSM after the user-agent side was detached for background fill. The server-to-cache tunnel should continue, but the stale user-agent event misses the VC table and falls through to the default tunnel handler, which tears down the SM with background_fill still STARTED. This ignores stale user-agent VIO and close events while a background fill tunnel is active, so tunnel_handler_server remains responsible for driving the fill to COMPLETED or ABORTED. This also keeps an unconditional kill_this cleanup as a last-resort guard to balance background_fill_current_count if any unexpected path still tears down mid-fill. (cherry picked from commit d366ab0feda439399bc32d8d3d2551492347fcb1) --- src/proxy/http/HttpSM.cc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc index ac2e1b657c..7a9c3bc8a5 100644 --- a/src/proxy/http/HttpSM.cc +++ b/src/proxy/http/HttpSM.cc @@ -2678,11 +2678,47 @@ HttpSM::main_handler(int event, void *data) } } + // Is there an active background-fill tunnel? + // Background fill detaches the user-agent VC and lets the server/cache side + // finish the response body. An Http2Stream can still deliver a scheduled UA + // close/error callback after that detach, but the UA VIO no longer has a VC + // table entry. This predicate recognizes only those detached UA-side events + // so they do not fall through to the default tunnel handler and tear down the + // HttpSM before the active background-fill tunnel completes. + auto is_stale_bg_fill_ua_event = [&]() -> bool { + if (background_fill != BACKGROUND_FILL_STARTED || !tunnel.is_tunnel_alive() || _ua.get_txn() == nullptr) { + return false; + } + + switch (event) { + case VC_EVENT_EOS: + case VC_EVENT_ERROR: + case VC_EVENT_INACTIVITY_TIMEOUT: + case VC_EVENT_ACTIVE_TIMEOUT: + case VC_EVENT_WRITE_READY: + case VC_EVENT_WRITE_COMPLETE: + case VC_EVENT_READ_READY: + case VC_EVENT_READ_COMPLETE: + break; + default: + return false; + } + + if (data == nullptr) { + return true; + } + + return static_cast<VIO *>(data)->vc_server == _ua.get_txn(); + }; + if (vc_entry) { jump_point = (static_cast<VIO *>(data) == vc_entry->read_vio) ? vc_entry->vc_read_handler : vc_entry->vc_write_handler; ink_assert(jump_point != (HttpSMHandler) nullptr); ink_assert(vc_entry->vc != (VConnection *)nullptr); (this->*jump_point)(event, data); + } else if (is_stale_bg_fill_ua_event()) { + SMDbg(dbg_ctl_http, "ignoring stale %s event for closed user agent while background fill is active", + HttpDebugNames::get_event_name(event)); } else { ink_assert(default_handler != (HttpSMHandler) nullptr); (this->*default_handler)(event, data); @@ -7587,6 +7623,16 @@ HttpSM::kill_this() // we must check it again if (kill_this_async_done == true) { pending_action = nullptr; + + // This should only be a last-resort cleanup path. A background fill is + // normally driven to COMPLETED or ABORTED by tunnel_handler_server, but + // any unexpected teardown must still balance the active fill gauge before + // optional stats/logging run. + if (background_fill == BACKGROUND_FILL_STARTED) { + background_fill = BACKGROUND_FILL_ABORTED; + Metrics::Gauge::decrement(http_rsb.background_fill_current_count); + } + if (t_state.http_config_param->enable_http_stats) { update_stats(); }
