Summary
When an HTTP/2 peer sends a DATA frame on a stream that ATS has already closed, Http2ConnectionState::rcv_data_frame() charges the payload against the connection receive window (decrement_local_rwnd() at the top of the function) and then discards the frame with a RST_STREAM(STREAM_CLOSED). Charging the bytes is what RFC 9113 section 6.9 requires, but no stream ever consumes them, so nothing calls restart_receiving() for those bytes and no connection-level WINDOW_UPDATE is ever sent for them.
Impact
A client that keeps sending DATA to closed streams can run the connection window down to zero and stall its own connection. The window is only restored when some other stream on that connection makes progress and triggers restart_receiving(), or on the every-128-frames path in Http2CommonSession. Well-behaved clients can also hit this accidentally: if ATS resets a stream while an upload is still in flight, the in-flight DATA is discarded and permanently deducted from the connection window as the client sees it.
This is scoped to the affected connection only; it does not affect other connections or users. It is a robustness bug, not a security issue.
Expected behavior
Discarded DATA should be credited back to the connection window promptly so the connection window stays synchronized between both endpoints and DATA to closed streams cannot drain it.
Reproduction
- Open an HTTP/2 connection and complete a request on stream 1.
- Send DATA frames on stream 1 (now closed), honoring the connection send window as advertised by ATS.
- After 65535 bytes the client's connection send window reaches zero and ATS never sends a connection
WINDOW_UPDATE. A subsequent request with a body cannot send its DATA.
An AuTest client that does this is included in the fix PR.
Summary
When an HTTP/2 peer sends a DATA frame on a stream that ATS has already closed,
Http2ConnectionState::rcv_data_frame()charges the payload against the connection receive window (decrement_local_rwnd()at the top of the function) and then discards the frame with aRST_STREAM(STREAM_CLOSED). Charging the bytes is what RFC 9113 section 6.9 requires, but no stream ever consumes them, so nothing callsrestart_receiving()for those bytes and no connection-levelWINDOW_UPDATEis ever sent for them.Impact
A client that keeps sending DATA to closed streams can run the connection window down to zero and stall its own connection. The window is only restored when some other stream on that connection makes progress and triggers
restart_receiving(), or on the every-128-frames path inHttp2CommonSession. Well-behaved clients can also hit this accidentally: if ATS resets a stream while an upload is still in flight, the in-flight DATA is discarded and permanently deducted from the connection window as the client sees it.This is scoped to the affected connection only; it does not affect other connections or users. It is a robustness bug, not a security issue.
Expected behavior
Discarded DATA should be credited back to the connection window promptly so the connection window stays synchronized between both endpoints and DATA to closed streams cannot drain it.
Reproduction
WINDOW_UPDATE. A subsequent request with a body cannot send its DATA.An AuTest client that does this is included in the fix PR.