Skip to content

C#: fix IndexOutOfRangeException in ReadRawByte on truncated messages#26914

Open
pawlos wants to merge 1 commit intoprotocolbuffers:mainfrom
pawlos:fix/csharp-readrawbyte-ioob-truncated-message
Open

C#: fix IndexOutOfRangeException in ReadRawByte on truncated messages#26914
pawlos wants to merge 1 commit intoprotocolbuffers:mainfrom
pawlos:fix/csharp-readrawbyte-ioob-truncated-message

Conversation

@pawlos
Copy link
Copy Markdown

@pawlos pawlos commented Apr 15, 2026

Summary
A near-int.MaxValue length varint overflows PushLimit, corrupting bufferSize to a negative value. ReadRawByte's == guard then never triggers RefillBuffer, causing an out-of-bounds read instead of InvalidProtocolBufferException.TruncatedMessage().

Fix
change == to >= in ReadRawByte. Regression tests added for all four affected slow-path variants.

Tests
Added TruncatedMessageWithLargeInnerLengthThrowsInvalidProtocolBufferException with 4 test cases.

Fixes #26856

@pawlos pawlos requested a review from a team as a code owner April 15, 2026 19:09
@pawlos pawlos requested review from jskeet and removed request for a team April 15, 2026 19:09
@google-cla
Copy link
Copy Markdown

google-cla Bot commented Apr 15, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@jskeet jskeet removed their request for review April 15, 2026 19:13
@jskeet
Copy link
Copy Markdown
Contributor

jskeet commented Apr 15, 2026

I'm not on the protobuf team and don't have time to review this right now. I'll get to it at some point if I can, but it won't be imminently.

@pawlos pawlos force-pushed the fix/csharp-readrawbyte-ioob-truncated-message branch from c21d591 to 8fcb410 Compare April 15, 2026 19:18
@jskeet
Copy link
Copy Markdown
Contributor

jskeet commented Apr 20, 2026

I've had a quick look at this now, and I'm not yet convinced the fix is right - if the bufferPos is greater than bufferSize, we've already done something wrong. Will try to reproduce locally with the new tests, and see if I can understand it better.

@pawlos
Copy link
Copy Markdown
Author

pawlos commented Apr 20, 2026

I've had a quick look at this now, and I'm not yet convinced the fix is right - if the bufferPos is greater than bufferSize, we've already done something wrong. Will try to reproduce locally with the new tests, and see if I can understand it better.

You're right. It probably just masks the real issue. I'll have another look too.

@jskeet
Copy link
Copy Markdown
Contributor

jskeet commented Apr 20, 2026

SegmentedBufferHelper.PushLimit calls SegmentedBufferHelper.RecomputeBufferSizeAfterLimit which is changing state.bufferSize to a negative value, because we "think" the length is so huge (so it overflows). Changing that to a checked block triggers an OverflowException instead - which suggests this is indeed the right place to look.

Stack of where I think things are going wrong:

  • SegmentedBufferHelper.RecomputeBufferSizeAfterLimit
  • SegmentedBufferHelper.PushLimit
  • ParsingPrimitivesMessages.ReadMessage
  • ParseContext.ReadMessage

I wasn't involved in writing SegmentedBufferHelper or ParserInternalState, so I'm less confident in working out what we should do here. Will need to spend some time understanding it better - but I'm pretty sure the fix should be in one of the first two methods.

@pawlos pawlos force-pushed the fix/csharp-readrawbyte-ioob-truncated-message branch from 8fcb410 to 75ae10e Compare April 24, 2026 15:24
@pawlos
Copy link
Copy Markdown
Author

pawlos commented Apr 24, 2026

I wasn't involved in writing SegmentedBufferHelper or ParserInternalState, so I'm less confident in working out what we should do here. Will need to spend some time understanding it better - but I'm pretty sure the fix should be in one of the first two methods.

Thanks for the pointer @jskeet! I've pushed a new commit that does the PushLimit arithmetic in long so state.currentLimit can't go negative, and reverted the >= guard in ReadRawByte. Went with widening over checked {} to keep the existing TruncatedMessage() exception path - hope this addresses the underlying issue.

throw InvalidProtocolBufferException.NegativeSize();
}
byteLimit += state.totalBytesRetired + state.bufferPos;
long absoluteLimit = (long)byteLimit + state.totalBytesRetired + state.bufferPos;
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.

Possibly add a comment to explain why we're doing this? (It looks right to me though.)

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.

@jskeet added

Compute the absolute limit in long arithmetic so overflow no longer
leaves state.currentLimit negative.

Fixes protocolbuffers#26856
@pawlos pawlos force-pushed the fix/csharp-readrawbyte-ioob-truncated-message branch from 75ae10e to 355e425 Compare April 24, 2026 15:43
Copy link
Copy Markdown
Contributor

@jskeet jskeet left a comment

Choose a reason for hiding this comment

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

That's great, thanks. I'll add another reviewer from the protobuf team.

@jskeet jskeet requested a review from mkruskal-google April 24, 2026 16:06
@jskeet
Copy link
Copy Markdown
Contributor

jskeet commented Apr 24, 2026

@mkruskal-google This looks good to me, and has appropriate tests.

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.

[C#] IndexOutOfRangeException instead of InvalidProtocolBufferException on truncated messages

2 participants