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

grpc: fix message length checks when compression is enabled and maxReceiveMessageSize is MaxInt #7918

Merged

Conversation

vinothkumarr227
Copy link
Contributor

@vinothkumarr227 vinothkumarr227 commented Dec 10, 2024

Fixes #4552

RELEASE NOTES:

  • grpc: fix message length checks when compression is enabled and maxReceiveMessageSize is MaxInt

@arjan-bal arjan-bal self-requested a review December 11, 2024 11:09
@arjan-bal arjan-bal self-assigned this Dec 11, 2024
@arjan-bal arjan-bal added Area: RPC Features Includes Compression, Encoding, Attributes/Metadata, Interceptors. Type: Bug and removed Area: RPC Features Includes Compression, Encoding, Attributes/Metadata, Interceptors. labels Dec 11, 2024
@arjan-bal arjan-bal added this to the 1.70 Release milestone Dec 11, 2024
Copy link

codecov bot commented Dec 12, 2024

Codecov Report

Attention: Patch coverage is 87.09677% with 4 lines in your changes missing coverage. Please review.

Project coverage is 82.03%. Comparing base (e8055ea) to head (6fb580c).
Report is 32 commits behind head on master.

Files with missing lines Patch % Lines
rpc_util.go 87.09% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7918      +/-   ##
==========================================
- Coverage   82.08%   82.03%   -0.05%     
==========================================
  Files         379      384       +5     
  Lines       38261    38751     +490     
==========================================
+ Hits        31406    31790     +384     
- Misses       5551     5634      +83     
- Partials     1304     1327      +23     
Files with missing lines Coverage Δ
rpc_util.go 80.67% <87.09%> (+1.35%) ⬆️

... and 36 files with indirect coverage changes

rpc_util.go Outdated
if err != nil {
out.Free()
return nil, 0, err
}
if err = checkReceiveMessageOverflow(int64(out.Len()), int64(maxReceiveMessageSize), dcReader); err != nil {
return nil, out.Len() + 1, err
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding 1 to out.Len() can result in an overflow on 32 bit systems. The following part of the function seems strange to me:

Optionally, if data will be over maxReceiveMessageSize, just return the size

I suggest making the following change to avoid this:

  1. Declare a global error for indicating that the max receive size is exceeded:
var	errMaxMessageSizeExceeded = errors.New("max message size exceeded")
  1. When the check here fails, nil, 0, errMaxMessageSizeExceeded. Update the godoc to mention the same.
  2. In the caller, i.e. recvAndDecompress, instead of checking if size > maxReceiveMessageSize, check if err == errMaxMessageSizeExceeded. Also update the error message to not mention the actual size, because we didn't read the entire message anyways: grpc: received message after decompression larger than max %d.

This ensures we're using the returned error to indicate the failure instead of relying on special values of the bytes read count.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please let the reviewer resolve comments.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should not attempt to add out.Len() + 1, instead return errMaxMessageSizeExceeded to signal the same.

Copy link
Contributor

Choose a reason for hiding this comment

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

wait, in case of overflow i think we are still reading the partial data so we need to return the length upto which we have read?

Copy link
Contributor

Choose a reason for hiding this comment

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

so, the logic should be read till maxMessageSize -1 first and then check if we still have bytes to read by reading one byte and if we overflow, throw the error along with lenght of how much we have read

rpc_util.go Outdated
if err != nil {
out.Free()
return nil, 0, err
}
if err = checkReceiveMessageOverflow(int64(out.Len()), int64(maxReceiveMessageSize), dcReader); err != nil {
return nil, out.Len() + 1, err
Copy link
Contributor

Choose a reason for hiding this comment

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

Please let the reviewer resolve comments.

rpc_util.go Outdated
if err != nil {
out.Free()
return nil, 0, err
}
if err = checkReceiveMessageOverflow(int64(out.Len()), int64(maxReceiveMessageSize), dcReader); err != nil {
return nil, out.Len() + 1, err
Copy link
Contributor

Choose a reason for hiding this comment

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

We should not attempt to add out.Len() + 1, instead return errMaxMessageSizeExceeded to signal the same.

@vinothkumarr227 vinothkumarr227 force-pushed the 4552-max-receive-message-size-decompression branch from 9d80990 to 3cf9054 Compare December 18, 2024 07:44
@vinothkumarr227 vinothkumarr227 removed their assignment Jan 10, 2025
@vinothkumarr227
Copy link
Contributor Author

@purnesh42H I mistakenly clicked on 'Unassign me' from the issue. Can you please reassign the issue to me?

@purnesh42H
Copy link
Contributor

@purnesh42H I mistakenly clicked on 'Unassign me' from the issue. Can you please reassign the issue to me?

Are you still working on resolving any remaining comments? If not, it should be assigned to Doug as 2nd reviewer to review. Regarding issue, you can comment here #4552 and we can assign to you.

@vinothkumarr227
Copy link
Contributor Author

@purnesh42H I have completed addressing all the review comments. The changes are ready for review

Copy link
Member

@dfawley dfawley left a comment

Choose a reason for hiding this comment

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

Mostly looks good, just a couple small things & a suggestion.

rpc_util.go Outdated
}
return out, nil
return d, nil
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this an error scenario? If the payload is compressed (which is the only reason to call this function) and we don't have the decompressor, then we should error.

In reality, this can't happen because checkRecvPayload would prevent it, but this should still return an error, not a success.

rpc_util.go Outdated
}

// doesReceiveMessageOverflow checks if the number of bytes read from the stream
// exceed the maximum receive message size allowed by the receiver. If the `readBytes`
Copy link
Member

Choose a reason for hiding this comment

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

Nit: "exceeds"

rpc_util.go Outdated
return nil, status.Errorf(codes.Internal, "grpc: failed to read decompressed data: %v", err)
}

if doesReceiveMessageOverflow(out.Len(), maxReceiveMessageSize, dcReader) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit:

How about something like this instead:

if out.Len() == maxReceiveMessageSize && !atEOF(dcReader) {
// atEOF reads data from r and returns true iff zero bytes could be read and r.Read returns EOF.
func atEOF(r io.Reader) bool {
	n, err := dcReader.Read(make([]byte, 1))
	return n == 0 && err == io.EOF
}

(Reasoning: "doesReceiveMessageOverflow" is very special-purpose, combines multiple operations into a single thing, and hides/splits relevant logic (reading & checking the length), whereas "atEOF" is potentially reusable, and does exactly one thing.)

@dfawley dfawley assigned vinothkumarr227 and unassigned dfawley Jan 16, 2025
@vinothkumarr227
Copy link
Contributor Author

@purnesh42H I have addressed all of Doug review comments. The changes are ready for a second review

@dfawley dfawley changed the title grpc: fix receiving empty messages when compression is enabled and maxReceiveMessageSize is MaxInt #7753 grpc: fix message length checks when compression is enabled and maxReceiveMessageSize is MaxInt Jan 23, 2025
@dfawley dfawley merged commit 8cf8fd1 into grpc:master Jan 23, 2025
15 checks passed
purnesh42H pushed a commit to purnesh42H/grpc-go that referenced this pull request Jan 30, 2025
janardhanvissa pushed a commit to janardhanvissa/grpc-go that referenced this pull request Feb 13, 2025
janardhanvissa pushed a commit to janardhanvissa/grpc-go that referenced this pull request Feb 13, 2025
janardhanvissa pushed a commit to janardhanvissa/grpc-go that referenced this pull request Feb 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Get empty response when compress enabled and maxReceiveMessageSize be maxInt64
8 participants