Skip to content

fix: don't overflow in BlobLocations::can_coalesce - #589

Open
Huge wants to merge 1 commit into
rustic-rs:mainfrom
Huge:fix/blob-locations-coalesce-overflow
Open

Huge wants to merge 1 commit into
rustic-rs:mainfrom
Huge:fix/blob-locations-coalesce-overflow

Conversation

@Huge

@Huge Huge commented Oct 6, 2026

Copy link
Copy Markdown

Problem

BlobLocations::can_coalesce computes self.offset + self.length + MAX_HOLESIZE and other.offset + other.length - self.offset in u32. The offsets and lengths come from the index files (and pack headers) and are not validated, so a corrupt or crafted entry close to u32::MAX makes these additions overflow:

  • with overflow checks enabled (debug / test builds) it panics with attempt to add with overflow,
  • in release builds the result wraps, so two locations can be judged coalescable (or not) wrongly. append then computes other.offset + other.length - self.offset for the combined partial read, which can wrap as well.

coalesce is used by restore, check, prune and copy to merge neighbouring blobs into a single partial pack read.

This is the same class of problem as #585 (unchecked u32 arithmetic on values taken from the index), in a different place.

Fix

Compute the end of both locations with checked_add and return false if either overflows. The three conditions are rewritten so that they cannot underflow (other.offset >= end is checked first, then other.offset - end <= MAX_HOLESIZE, and other_end - self.offset is safe because other_end >= other.offset >= end >= self.offset). For values that do not overflow they are equivalent to the old conditions. append is only reached after can_coalesce succeeded, so its other.offset + other.length - self.offset can no longer overflow either.

Tests

test_coalesce gets four more cases at the end of the u32 range: a valid coalesce right below u32::MAX, and three cases where the end of the first/second location overflows. The existing 11 cases are unchanged and still pass. Before the change the new cases fail with attempt to add with overflow at blob.rs:187.

cargo fmt --check and cargo clippy are clean for the touched code. cargo test --workspace is green apart from five snapshot tests in tests/integration (backup, chunker, rewrite) that fail identically on an unmodified main because of an error_count field in the snapshots; they are unrelated to this change.

Disclaimer: Found the issue by test-driving an LM-powered vulnerability scanner I work on heavily.

`can_coalesce` added up `offset`, `length` and `MAX_HOLESIZE` of blob
locations in `u32`. Offsets and lengths come from the index files and
are not validated, so entries close to `u32::MAX` made the additions
overflow: a panic with overflow checks enabled, and a wrapped (and thus
wrong) coalescing decision in release builds. A wrongly coalesced pair
makes `append` compute a bogus read length for the partial pack read
used by restore, check, prune and copy.

Use `checked_add` for the end of both locations and never coalesce when
it overflows. The remaining comparisons are rewritten in a form that
cannot underflow; they are equivalent to the previous ones for
non-overflowing values. As `append` is only called after `can_coalesce`
succeeded, its arithmetic is covered as well.

Extend `test_coalesce` with cases at the end of the `u32` range.
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.

1 participant