Skip to content

RFC 9113: limit the number of pending control frames - #731

Closed
arturobernalg wants to merge 1 commit into
apache:masterfrom
arturobernalg:h2-max-control-frames
Closed

arturobernalg wants to merge 1 commit into
apache:masterfrom
arturobernalg:h2-max-control-frames

Conversation

@arturobernalg

@arturobernalg arturobernalg commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

PING and SETTINGS acknowledgements, WINDOW_UPDATE and RST_STREAM frames generated in response to peer frames were queued without bound when the output could not be flushed as fast as the peer sent them.

Measured on master with a single connection and the default H2ServerBootstrap configuration, 1,500,000 PING frames (about 25 MB): the output queue reached 1.3 to 1.5 million entries and the heap grew by 170 to 194 MB. The same happens when the peer reads the responses, fast or slowly, as the input is processed faster than the output is written. SETTINGS frames behave the same. Empty DATA frames do not amplify, as WINDOW_UPDATE is throttled by the flow control low mark.

AbstractH2StreamMultiplexer now counts the control frames pending output and closes the connection with a connection error of type ENHANCE_YOUR_CALM beyond H2Config#getMaxControlFrames, 1000 by default, zero disables the limit. RFC 9113 section 10.5 says implementations SHOULD track the use of these features and set limits, and MAY treat such activity as ENHANCE_YOUR_CALM. The counter holds the frames currently queued and goes down as they are flushed, so a peer that keeps up is never limited. It follows maxContinuations.

The GOAWAY is sent when the output still permits it. A peer that has stopped reading, which is the attack case, makes the queue full and the socket blocked, so the connection is closed immediately and the peer sees a reset; the queue stays at the limit and the heap stays flat. The existing maxContinuations path behaves the same way.

H2Config#copy dropped maxContinuations; it is copied now along with the new setting.

Rapid reset (HEADERS followed by RST_STREAM repeated) is not addressed here. A single connection can force tens of thousands of stream setup and teardown cycles per second without reset accounting; that needs separate inbound accounting.

@arturobernalg
arturobernalg requested a review from ok2c November 1, 2026 12:25
@arturobernalg
arturobernalg marked this pull request as draft November 1, 2026 13:47
PING and SETTINGS acknowledgements, WINDOW_UPDATE and RST_STREAM frames
generated in response to peer frames were queued without bound when the
output could not be flushed as fast as the peer sent them. A single
connection sending PING frames made the server queue over a million
acknowledgements and retain about 170 MB of heap.

AbstractH2StreamMultiplexer now counts the control frames pending output
and closes the connection with a connection error of type
ENHANCE_YOUR_CALM beyond H2Config#getMaxControlFrames, 1000 by default,
zero disables the limit. The GOAWAY is sent when the output still permits
it; a peer that has stopped reading forces an immediate close.

H2Config#copy now also copies maxContinuations, which it dropped.
@ok2c

ok2c commented Nov 1, 2026

Copy link
Copy Markdown
Member

@arturobernalg I am not sure this is conceptually correct. The problem is not the absolute number of frames but a ratio per period of time. I am not in favor of this change.

I have started working on H2 frame flood detection and prevention but never managed to finish it because nowadays 90% of my time is being spent reviewing pull requests.

Please prioritize pull requests and bug reports for the components you have already contributed over new features or adding more code.

@arturobernalg

Copy link
Copy Markdown
Member Author

Thanks, Oleg. Agreed, a rate per period of time is the right model and not an absolute count. I am closing this one and leave the H2 frame flood handling to your work. The reproducer (a single connection sending PING frames makes the server queue over a million acknowledgements and retain about 170 MB of heap) is available if it is useful to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants