From 6ff78d38cce51461bf0ca23c23c145f3681601c9 Mon Sep 17 00:00:00 2001 From: bneradt Date: Wed, 5 Aug 2026 17:31:29 -0500 Subject: [PATCH] Avoid stale H2 writes after 100 Continue HttpSM owns the write buffer attached to an HTTP/2 stream. After WRITE_COMPLETE it may release the buffer while a connection-level write-ready event can restart the stream through the non-owning _send_reader alias. This leaves restart_sending vulnerable to a use-after-free. Clear _send_reader before delivering WRITE_COMPLETE to HttpSM, and check completed write VIOs before inspecting the reader during connection restarts. This preserves zero-byte completion processing, including END_STREAM. --- src/proxy/http2/Http2Stream.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/proxy/http2/Http2Stream.cc b/src/proxy/http2/Http2Stream.cc index 6be67db03b9..611e8b6a4eb 100644 --- a/src/proxy/http2/Http2Stream.cc +++ b/src/proxy/http2/Http2Stream.cc @@ -807,12 +807,12 @@ Http2Stream::restart_sending() } } - IOBufferReader *reader = this->get_data_reader_for_send(); - if (reader && !reader->is_read_avail_more_than(0)) { + if (this->write_vio.mutex && this->write_vio.ntodo() <= 0) { return; } - if (this->write_vio.mutex && this->write_vio.ntodo() == 0) { + IOBufferReader *reader = this->get_data_reader_for_send(); + if (reader && !reader->is_read_avail_more_than(0)) { return; } @@ -970,6 +970,11 @@ Http2Stream::signal_write_event(int event, bool call_update) write_event = nullptr; } _timeout.update_inactivity(); + if (event == VC_EVENT_WRITE_COMPLETE) { + // HttpSM owns the write buffer and may release it while handling this + // event. Drop the unowned alias before transferring control. + _send_reader = nullptr; + } this->write_vio.cont->handleEvent(event, &this->write_vio); } else { if (this->_write_vio_event) {