Skip to content

reject zero-length padded DATA frame in H2Context::OnData#3325

Open
sahvx655-wq wants to merge 1 commit into
apache:masterfrom
sahvx655-wq:h2-data-pad-underflow
Open

reject zero-length padded DATA frame in H2Context::OnData#3325
sahvx655-wq wants to merge 1 commit into
apache:masterfrom
sahvx655-wq:h2-data-pad-underflow

Conversation

@sahvx655-wq

Copy link
Copy Markdown

A DATA frame with the PADDED flag but Length 0 underflows frag_size here. --frag_size wraps the uint32_t to 0xFFFFFFFF, the frag_size < pad_length check then passes, and the bogus size reaches append_and_forward, which swallows the rest of the connection buffer as one DATA payload and feeds ~4GiB into the flow-control counter. OnHeaders already rejects this case; OnData was missing the same guard. Fix returns FRAME_SIZE_ERROR per RFC 7540 6.1 when a padded frame has no room for the pad-length byte.

Comment thread src/brpc/policy/http2_rpc_protocol.cpp Outdated
H2ParseResult H2Context::OnData(
butil::IOBufBytesIterator& it, const H2FrameHead& frame_head) {
const bool has_padding = (frame_head.flags & H2_FLAGS_PADDED);
if (frame_head.payload_size < (size_t)has_padding) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The logic is not clear to converter a bool to a size_t here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agreed, the bool to size_t cast was too cute and obscured what it was actually testing. I've removed it in favour of the explicit zero check inside the PADDED branch below.

Comment thread src/brpc/policy/http2_rpc_protocol.cpp Outdated
uint32_t frag_size = frame_head.payload_size;
uint8_t pad_length = 0;
if (frame_head.flags & H2_FLAGS_PADDED) {
if (has_padding) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think a better way is to add the check code here:

if (frag_size <= 0) {
  LOG(ERROR) << ...
  return ...;
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done, moved the check inside the PADDED branch as you suggested. One small tweak: I used frag_size == 0 rather than <= 0, since frag_size is uint32_t and -W flags the <= 0 form as always-false. Same effect, just keeps the build warning-free.

It reads cleaner there anyway, the zero-length case only matters when PADDED is set and we're about to decrement for the pad-length byte. The check sits before --frag_size, so a payload_size of 0 with PADDED now returns FRAME_SIZE_ERROR rather than wrapping frag_size to 0xFFFFFFFF and slipping past the frag_size < pad_length check. An empty unpadded DATA frame still passes through untouched.

@sahvx655-wq sahvx655-wq force-pushed the h2-data-pad-underflow branch from a25df47 to b877235 Compare June 12, 2026 12:28
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