Skip to content

cap varint length in DecodeVarint to avoid undefined shift#2106

Open
nabhan06 wants to merge 2 commits into
abseil:masterfrom
nabhan06:proto-varint-shift-cap
Open

cap varint length in DecodeVarint to avoid undefined shift#2106
nabhan06 wants to merge 2 commits into
abseil:masterfrom
nabhan06:proto-varint-shift-cap

Conversation

@nabhan06

Copy link
Copy Markdown
Contributor

DecodeVarint in absl/log/internal/proto.cc accumulates a base-128 varint with value |= (byte & 0x7f) << 7 * s but never caps the byte index, unlike the sibling Decode32Bit and Decode64Bit helpers that stop at sizeof(value). A malformed or over-long buffer whose first 10 bytes all set the continuation bit drives the shift to << 70 on a 64-bit value, which UBSan flags as an undefined shift exponent. This caps the loop at the 10-byte maximum of a 64-bit varint, matching the sibling decoders, with no change for well-formed input.

@mkruskal-google

Copy link
Copy Markdown
Contributor

Looks reasonable, can you add a test case that would have caught this too?

@mkruskal-google mkruskal-google self-assigned this Jul 11, 2026
@mkruskal-google mkruskal-google self-requested a review July 11, 2026 21:00
@nabhan06

Copy link
Copy Markdown
Contributor Author

Added proto_test.cc with a DecodeVarint case: a varint field whose bytes all set the continuation bit. Without the cap that drives the shift to << 70 on a uint64_t, so it fails under UBSan (shift exponent 70 too large, abort); with the cap it decodes fine and consumes at most the 10 varint bytes. Also wired the target into BUILD.bazel and CMakeLists.txt, plus a round-trip case as a sanity check.

Comment on lines 15 to 17
// All of the data that passes through this code is trusted because it flows
// through a closed loop within the absl::LogMessage object. It is not a robust
// protocol buffer encoder or decoder.

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.

Sorry just noticed this comment when I was wondering where this was even called from. What issue did you hit with this? This is definitely not a hardened protobuf parser

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.

When I was asking for a test I was more wondering if this was reachable from public APIs. This unit-test illustrated the theoretical problem, but it uses internal APIs that I suspect aren't actually reachable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No production crash behind it. I hit the << 70 while running the log internal code under UBSan and feeding DecodeFrom a deliberately malformed span (a varint field followed by 10 bytes that all set the continuation bit). Fair point that this isn't a hardened parser, but the loop already runs up to buf->size() specifically to tolerate junk input, and Decode32Bit/Decode64Bit right below already cap their loops to avoid the same over-wide shift. This just makes DecodeVarint consistent with them. If you'd rather not touch this path at all I'm fine closing it.

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.

3 participants