Uh oh!
There was an error while loading. Please reload this page.
Add NDEBUG-safe boundary checks in SnappyIOVecReader::Advance - #237
Add NDEBUG-safe boundary checks in SnappyIOVecReader::Advance#237SongT-50 wants to merge 1 commit into
Conversation
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. |
de1899c to
4be8f6fCompareSongT-50
commented
Jul 6, 2026
Hi — a gentle ping on this PR. I know maintainers are busy, so no rush at all. If there's anything I can do to help it move forward (rebase, extra tests, or clarifying the boundary-check rationale), I'm happy to. Thanks for maintaining snappy! |
The current Advance() relies on a debug-only assert to enforce the invariant total_size_remaining_ >= curr_size_remaining_. Under NDEBUG (most production builds) the assert is stripped, leaving the inner do-while loop unguarded against caller-misuse of the raw API RawCompressFromIOVec(iov, uncompressed_length, ...) when uncompressed_length does not match the actual sum of iov_len values. Two defensive checks are added (interim, NDEBUG-safe): 1. If total_size_remaining_ < curr_size_remaining_ on entry, saturate to no-data state and bail out rather than underflowing total_size_remaining_ into a large unsigned value below. 2. Cap consecutive zero-length iovec walks at kMaxEmptyWalks (1024). A run of zero-length trailing entries with nonzero claimed total_size would otherwise spin past the array end via the do-while loop. The default safe wrapper CompressFromIOVec(iov, iov_cnt, ...) already computes the total itself before calling the raw API and is unaffected by this change. The patch is backward compatible (API signature unchanged). A more permanent API-level fix is to add a safe overload of RawCompressFromIOVec that takes iov_cnt explicitly and deprecate the current 3-arg form. Happy to prepare a follow-up PR for that direction if maintainers prefer. Findings cross-verified through independent code auditing (multi-model code review pipeline).
4be8f6f to
87425bcCompareSongT-50
commented
Jul 31, 2026
Hi @danilak-G — sorry to ping you directly. I saw you reviewed and merged #242 recently, so I hope it's alright to ask here. Status: CLA signed, check-changes passing, no conflicts. Short version: under NDEBUG the only bounds guard in Advance() is a debug-only assert, I'm not attached to the specific numbers — kMaxEmptyWalks is a placeholder and I'd happily No rush at all, and please ignore this if it's not a priority. Thanks for maintaining snappy. |
Summary
Under
NDEBUG(release builds)SnappyIOVecReader::Advance()(snappy.cc:1937) loses itsonly bounds guard — a debug-only
assert(total_size_remaining_ >= curr_size_remaining_).When a caller of the raw API
RawCompressFromIOVec(iov, uncompressed_length, ...)passes anuncompressed_lengthlarger than the actual sum ofiov_len,Advance()walkscurr_iov_past the end of the iovec array (OOB read / DoS). A run of zero-length trailing entries
triggers the same walk.
The safe public wrapper
CompressFromIOVec(iov, iov_cnt, ...)computes the total itself andis unaffected — only direct raw-API callers are exposed.
Fix
Two NDEBUG-safe guards in
Advance()(noassertreliance; API signature unchanged; normalinputs take the same path):
total_size_remaining_ < curr_size_remaining_(prevents the unsigned underflow that leads to the past-end walk).
kMaxEmptyWalks;1024is a placeholder — maintainersmay pick a value that fits the project's convention).
Test
Three edge cases (normal /
total_size > sum(iov_len)/ zero-length trailing) build clean under-fsanitize=address,undefined; a standalone repro is available on request, and I'm happy to adda
snappy_unittestcase.A permanent API-level fix (add an
iov_cntoverload and deprecate the 3-arg raw API) is thecleaner direction — happy to follow up if you prefer that instead of / in addition to this interim guard.