Skip to content

Fix HTTP/3 crash in HQTransaction::_signal_event#13093

Merged
bneradt merged 1 commit into
apache:masterfrom
bneradt:fix-h3-reset
Apr 22, 2026
Merged

Fix HTTP/3 crash in HQTransaction::_signal_event#13093
bneradt merged 1 commit into
apache:masterfrom
bneradt:fix-h3-reset

Conversation

@bneradt
Copy link
Copy Markdown
Contributor

@bneradt bneradt commented Apr 16, 2026

I'm running this is docs now and I have no longer seen this rather frequent
crash.


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

@bneradt bneradt added this to the 11.0.0 milestone Apr 16, 2026
@bneradt bneradt self-assigned this Apr 16, 2026
@bneradt bneradt requested a review from maskit April 16, 2026 17:09
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: apache#12112
@bneradt bneradt changed the title Fix HTTP/3 crash in _signal_event Fix HTTP/3 crash in HQTransaction::_signal_event Apr 16, 2026
Comment on lines +362 to +366
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);
Copy link
Copy Markdown
Contributor Author

@bneradt bneradt Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look at src/proxy/http2/Http2Stream.cc, everywhere where handleEvent is called, we pass a vio, not edata. This seems to confirm what Claude did here. And, as mentioned in the description, I no longer see these crashes anymore on docs with this change.

@bneradt bneradt merged commit 801e8d7 into apache:master Apr 22, 2026
15 checks passed
@bneradt bneradt deleted the fix-h3-reset branch April 22, 2026 20:41
@github-project-automation github-project-automation Bot moved this to For v10.2.0 in ATS v10.2.x Apr 22, 2026
@cmcfarlen cmcfarlen moved this from For v10.2.0 to Picked v10.2.0 in ATS v10.2.x Apr 28, 2026
@cmcfarlen cmcfarlen modified the milestones: 11.0.0, 10.2.0 Apr 28, 2026
@cmcfarlen
Copy link
Copy Markdown
Contributor

Cherry-picked to 10.2.x

cmcfarlen pushed a commit that referenced this pull request Apr 29, 2026
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
(cherry picked from commit 801e8d7)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Picked v10.2.0

Development

Successfully merging this pull request may close these issues.

HttpSM.cc:2627: failed assertion default_handler != (HttpSMHandler) nullptr

3 participants