Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from Jan 18, 2024

Conversation

minwoox
Copy link
Contributor

@minwoox minwoox commented Jan 17, 2024

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
Copy link
Contributor

franz1981 commented Jan 17, 2024

However, it fails to slice the byteBuf during usage

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

@normanmaurer
Copy link
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
Copy link
Contributor

franz1981 commented Jan 17, 2024

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

Copy link
Contributor

@franz1981 franz1981 left a comment

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
Contributor

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

@Lincong
Copy link
Contributor

Lincong commented Jan 17, 2024

Hi @franz1981

I was just curious to know how he found it

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

@franz1981
Copy link
Contributor

franz1981 commented Jan 17, 2024

Many thanks for sharing @Lincong !!!

@Lincong
Copy link
Contributor

Lincong commented Jan 17, 2024

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
Copy link
Contributor Author

minwoox commented Jan 18, 2024

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
15 checks passed
@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.

None yet

6 participants