From 4a0de27ac19a5c1bac5616316684e5717d3369a4 Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Thu, 16 Apr 2026 01:19:03 +0000 Subject: [PATCH] Fix HTTP/3 crash in HQTransaction::_signal_event This fixes a crash where the assertion at HttpSM.cc:2765 fails: ink_assert(default_handler != (HttpSMHandler) nullptr) The crash is triggered when VC_EVENT_EOS, VC_EVENT_ERROR, or a timeout propagates from the QUIC stream up to HQTransaction::state_stream_open, which calls _signal_event. The previous implementation forwarded the triggering Event* as the data argument to HttpSM::handleEvent. HttpSM::main_handler, however, casts that data to VIO* and uses it to look up the vc_table entry. Because an Event* never matches any registered VIO, find_entry returns null and main_handler falls through to default_handler. When the SM is in early setup or has already been torn down (kill_this clears default_handler), default_handler is null and the assertion fires. This patch passes the appropriate VIO pointer to each handleEvent call, matching the convention used by _signal_read_event, _signal_write_event, and the HTTP/2 Http2Stream equivalents. The original dual-VIO dispatch is preserved so tunnel consumers bound to the write side still receive connection-level events. Fixes: #12112 --- src/proxy/http3/Http3Transaction.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/proxy/http3/Http3Transaction.cc b/src/proxy/http3/Http3Transaction.cc index 4bff46ab77a..8cdb7c3aead 100644 --- a/src/proxy/http3/Http3Transaction.cc +++ b/src/proxy/http3/Http3Transaction.cc @@ -353,15 +353,17 @@ HQTransaction::_close_write_complete_event(Event *e) } void -HQTransaction::_signal_event(int event, Event *edata) +HQTransaction::_signal_event(int event, Event * /* edata ATS_UNUSED */) { + // HttpSM::main_handler expects a VIO* as the event data for VC events so it + // can locate the vc_table entry. if (this->_write_vio.cont) { SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); - this->_write_vio.cont->handleEvent(event, edata); + this->_write_vio.cont->handleEvent(event, &this->_write_vio); } if (this->_read_vio.cont && this->_read_vio.cont != this->_write_vio.cont) { SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread()); - this->_read_vio.cont->handleEvent(event, edata); + this->_read_vio.cont->handleEvent(event, &this->_read_vio); } }