Conversation
RFC 9113 Section 5.1 requires an endpoint to treat frames other than PRIORITY, WINDOW_UPDATE and RST_STREAM received on a stream in the "half-closed (remote)" state as an error. Envoy's HTTP/2 codec did not enforce this: onHeaders() and onBeginData() unconditionally overwrote remote_end_stream_ and dispatched the frame to the stream's decoder. On a client connection that decoder may already be gone. CodecClient::completeRequest() deferred-deletes the ActiveRequest that owns the ResponseDecoder as soon as the response completes, while oghttp2 keeps the stream in its map until it is also half-closed locally -- for example when the upstream pins the stream send window to zero so that Envoy's request body is never written. A subsequent HEADERS or DATA frame from that upstream is then dispatched through a dangling reference, which aborts the process. nghttp2 already rejects these frames, so this only changes behavior for oghttp2. The check is guarded by envoy.reloadable_features.http2_reject_frames_after_end_stream, latched per connection to keep a runtime lookup off the frame path. Signed-off-by: Jonathan Wu <jtwu@google.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Commit Message: http2: reject HEADERS and DATA frames received after END_STREAM
Additional Description:
RFC 9113 Section 5.1 requires an endpoint to treat frames other than PRIORITY, WINDOW_UPDATE and
RST_STREAM received on a stream in the "half-closed (remote)" state as an error. Envoy's HTTP/2
codec did not enforce this:
ConnectionImpl::onHeaders()andConnectionImpl::onBeginData()unconditionally overwrote
remote_end_stream_and dispatched the frame to the stream's decoder.On a client connection that decoder may already be gone.
CodecClient::completeRequest()deferred-deletes the
ActiveRequestthat owns theResponseDecoderas soon as the responsecompletes, while oghttp2 keeps the stream in its map until it is also half-closed locally. An
upstream can arrange exactly that by pinning the stream send window to zero
(
SETTINGS INITIAL_WINDOW_SIZE=0) so Envoy's request body is never written:half_closed_localstays false, the response completes, and a subsequent HEADERS or DATA frame on the same stream is
dispatched through a dangling reference, aborting the process.
nghttp2 already rejects these frames, so this only changes behavior for oghttp2. A conforming peer
never sends frames after ending a stream and sees no change.
The check is placed in the codec rather than in the adapter so the invariant "no decoder callback
after remote END_STREAM" holds regardless of the underlying HTTP/2 library, and the runtime value
is latched per connection to keep a runtime lookup off the frame path.
Risk Level: Low. Guarded by
envoy.reloadable_features.http2_reject_frames_after_end_stream(default true); setting it to false restores the previous behavior.
Testing:
test/integration/http2_integration_test.cc, parameterized overIPv4/IPv6 x nghttp2/oghttp2:
DownstreamHeadersAfterEndStream/DownstreamDataAfterEndStream: a client that sends asecond HEADERS or a DATA frame after ending its stream is rejected.
UpstreamHeadersAfterEndStream: the zero-window case described above; Envoy now reports anupstream protocol error instead of dispatching into a destroyed decoder.
DownstreamHeadersAfterEndStreamGuardDisabled: guard-off coverage (nghttp2 only, since withthe guard off oghttp2 exhibits the defect being fixed).
UpstreamHeadersAfterEndStreamaborts withlibc++abi: Pure virtual function called!while dispatching trailers on a stream whoseremote_end_stream_is already set, confirming the test covers the regression.//test/integration:http2_integration_test,//test/integration:multiplexed_integration_testand
//test/common/http/http2:codec_impl_testall pass.Docs Changes: n/a
Release Notes: changelogs/current/bug_fixes/http2__reject-frames-after-end-stream.rst
Platform Specific Features: n/a