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

codec: Fix panic in LengthDelimitedCodec::encode #682

Merged
merged 3 commits into from
Oct 4, 2018

Conversation

hawkw
Copy link
Member

@hawkw hawkw commented Oct 2, 2018

Fixes: #681

Motivation

Currently, a potential panic exists in LengthDelimitedCodec::encode.
Writing the length field to the dst buffer can exceed the buffer
capacity, as BufMut::put_uint_{le,be} doesn't reserve more capacity.

Solution

This branch adds a check that there's sufficient remaining buffer
capacity to hold the length field, and resizes the buffer to hold the
length field if there is no capacity left.

I've also added a test that reproduces the issue. The test panics on
master, but passes after making this change.

Signed-off-by: Eliza Weisman eliza@buoyant.io

@hawkw hawkw added the C-bug Category: This is a bug. label Oct 2, 2018
@hawkw hawkw self-assigned this Oct 2, 2018
// If there's no room to write the length field in `dst`, reserve more
// capacity so we don't overflow the buffer.
if dst.remaining_mut() < self.builder.length_field_len {
dst.reserve(self.builder.length_field_len);
Copy link
Member Author

Choose a reason for hiding this comment

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

As a potential optimization, we might want to reserve enough buffer capacity for both the length field and the frame here.

The call to BytesMut::extend_from_slice on line 589 later on won't panic, as that method will resize the buffer if there's insufficient capacity to fit the frame. However, if we grow the buffer enough to fit the frame here, we might be able to do a single allocation rather than two?

Copy link
Member

Choose a reason for hiding this comment

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

I think doing the one allocation is preferential to (potentially) two.

Copy link
Member

Choose a reason for hiding this comment

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

I agree, since the total needed size is known, it should be reserved up front.

Also, you do not need to guard the reserve function.

Copy link
Member Author

@hawkw hawkw Oct 4, 2018

Choose a reason for hiding this comment

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

Okay, thanks @carllerche and @tobz for confirming my suspicion that reserving all the necessary capacity here was the Right Thing 😄 --- I've made that change in 256fa57.

Also, I think that since we're reserving sufficient capacity here, the BytesMut::extend_from_slice on line 583 could just be changed to a BufMut::push_slice --- @carllerche, is there significant additional overhead in calling extend_from_slice when the BytesMut has sufficient capacity for the slice?

@hawkw hawkw changed the title Eliza/fix length delimited codec: Fix panic in LengthDelimitedCodec::encode Oct 2, 2018
tobz
tobz previously requested changes Oct 4, 2018
Copy link
Member

@tobz tobz left a comment

Choose a reason for hiding this comment

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

I'm good with it if we have it do the allocation up front. 👍

// If there's no room to write the length field in `dst`, reserve more
// capacity so we don't overflow the buffer.
if dst.remaining_mut() < self.builder.length_field_len {
dst.reserve(self.builder.length_field_len);
Copy link
Member

Choose a reason for hiding this comment

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

I think doing the one allocation is preferential to (potentially) two.

// If there's no room to write the length field in `dst`, reserve more
// capacity so we don't overflow the buffer.
if dst.remaining_mut() < self.builder.length_field_len {
dst.reserve(self.builder.length_field_len);
Copy link
Member

Choose a reason for hiding this comment

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

I agree, since the total needed size is known, it should be reserved up front.

Also, you do not need to guard the reserve function.

@hawkw hawkw dismissed tobz’s stale review October 4, 2018 18:39

made all requested changes in 256fa57

@hawkw hawkw merged commit 1879bc4 into master Oct 4, 2018
@carllerche carllerche deleted the eliza/fix-length-delimited branch October 24, 2018 03:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants