Skip to content

Prevent sharing the index of the continuation frame header ByteBuf. - #13786

Merged
normanmaurer merged 1 commit into
netty:4.1from
minwoox:fix_header_frame
Jan 18, 2024
Merged

normanmaurer merged 1 commit into
netty:4.1from
minwoox:fix_header_frame

Conversation

@minwoox

@minwoox minwoox commented Jan 17, 2024

Copy link
Copy Markdown
Contributor

Motivation:
The current implementation uses the byteBuf for a continuation frame header multiple times if the header length exceeds 3 * maxFrameLength. However, it fails to slice the byteBuf during usage. Reference

Modification:

  • Introduce ByteBuf.retainedSlice() for a continuation frame header when it's used to prevent sharing the index.

Result:

  • Correctly send continuation frame headers to the remote peer, addressing the issue of reusing the index of the ByteBuf.

Motivation:
The current implementation uses the `byteBuf` for a continuation frame header multiple times if the header length exceeds `3 * maxFrameLength`. However, it fails to slice the `byteBuf` during usage.
[Reference](https://github.com/netty/netty/blob/d027ba7320d430743992d613e52596b0182ca854/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java#L570)

Modification:
- Introduce `ByteBuf.retainedSlice()` for a continuation frame header when it's used to prevent sharing the index.

Result:
- Correctly send continuation frame headers to the remote peer, addressing the issue of reusing the index of the ByteBuf.
@franz1981

franz1981 commented Jan 17, 2024

Copy link
Copy Markdown
Contributor

However, it fails to slice the byteBuf during usage

In which occasion it happens? what means "usage" in this context?

@normanmaurer

Copy link
Copy Markdown
Member

@franz1981 I think this fix is correct as you need to either call slice() or duplicate() if you pass the same buffer down the pipeline multiple times as otherwise you might end up with a buffer that had its indexes modified by a previous write operation.

@minwoox great catch! Did you sign our icla yet ? https://netty.io/s/icla

@franz1981

franz1981 commented Jan 17, 2024

Copy link
Copy Markdown
Contributor

Yep @normanmaurer I was just curious to know how he found it and how we didn't have coverage for that (including our vertx test suite) - cc @vietj

@franz1981 franz1981 left a comment

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.

Thanks and well done!

@alexc-db

Copy link
Copy Markdown
Contributor

hi all, would it be possible to cut a Netty release after this PR is merged? thanks!

@Lincong

Lincong commented Jan 17, 2024

Copy link
Copy Markdown
Contributor

Hi @franz1981

I was just curious to know how he found it

This is how we discovered it: line/armeria#5385

@franz1981

franz1981 commented Jan 17, 2024

Copy link
Copy Markdown
Contributor

Many thanks for sharing @Lincong !!!

@Lincong

Lincong commented Jan 17, 2024

Copy link
Copy Markdown
Contributor

I think this fix is correct as you need to either call slice() or duplicate() if you pass the same buffer down the pipeline multiple times as otherwise you might end up with a buffer that had its indexes modified by a previous write operation.

I used my debugger to confirm that this is the case.

In AbstractCoalescingBufferQueue, this is the point before sending the first CONTINUATION frame header:
image

After sending the first CONTINUATION frame header. This is the point before sending the second CONTINUATION frame header. As we can see, there is no more readable bytes in the header:

image

All readable bytes from the 1st CONTINUATION frame header ByteBuf is fully consumed [here]:(

newCumulation.writeBytes(cumulation).writeBytes(next);
):
image

After being copied to the cumulation ByteBuf, it contains no readable bytes:
image

@minwoox

minwoox commented Jan 18, 2024

Copy link
Copy Markdown
Contributor Author

Did you sign our icla yet ? netty.io/s/icla

Done it. 😉

Thanks @Lincong for sharing the issue. 😉

how we didn't have coverage for that (including our vertx test suite)

It only happens when TLS is used and the header length exceeds 3 * maxFrameLength. That's why it isn't found yet. (Armeria also does not cover this case.)

When TLS is used, the byteBuf is read in the queue of the SslHandler as @Lincong illustrated.

When TLS is not used, the reader and writer indexes of the byteBuf are used directly instead of reading the bytebuf in the ChannelOutboudBuffer so we couldn't find that.

@normanmaurer
normanmaurer merged commit d9ca50d into netty:4.1 Jan 18, 2024
@normanmaurer normanmaurer added this to the 4.1.106.Final milestone Jan 18, 2024
normanmaurer pushed a commit that referenced this pull request Jan 18, 2024
…13786)

Motivation:
The current implementation uses the `byteBuf` for a continuation frame
header multiple times if the header length exceeds `3 * maxFrameLength`.
However, it fails to slice the `byteBuf` during usage.
[Reference](https://github.com/netty/netty/blob/d027ba7320d430743992d613e52596b0182ca854/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java#L570)

Modification:
- Introduce `ByteBuf.retainedSlice()` for a continuation frame header
when it's used to prevent sharing the index.

Result:
- Correctly send continuation frame headers to the remote peer,
addressing the issue of reusing the index of the ByteBuf.
@minwoox
minwoox deleted the fix_header_frame branch January 19, 2024 06:47
franz1981 pushed a commit to franz1981/netty that referenced this pull request Feb 9, 2024
…etty#13786)

Motivation:
The current implementation uses the `byteBuf` for a continuation frame
header multiple times if the header length exceeds `3 * maxFrameLength`.
However, it fails to slice the `byteBuf` during usage.
[Reference](https://github.com/netty/netty/blob/d027ba7320d430743992d613e52596b0182ca854/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java#L570)

Modification:
- Introduce `ByteBuf.retainedSlice()` for a continuation frame header
when it's used to prevent sharing the index.

Result:
- Correctly send continuation frame headers to the remote peer,
addressing the issue of reusing the index of the ByteBuf.
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.

6 participants