Skip to content

fix(encoding): backport corruption-safety fixes to release/v7.1 - #8156

Merged
Xuanwo merged 3 commits into
release/v7.1from
backport/8138-8144-to-release-v7-1
Aug 3, 2026
Merged

fix(encoding): backport corruption-safety fixes to release/v7.1#8156
Xuanwo merged 3 commits into
release/v7.1from
backport/8138-8144-to-release-v7-1

Conversation

@Xuanwo

@Xuanwo Xuanwo commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Backport the corruption-safety fixes from:

The v7.1 stable line still reaches the vulnerable VariableFullZip length-prefix parser and unchecked variable-width offset paths through the default reader.

Release-specific adaptation: use the v7.1 error-construction and asynchronous reader APIs; retain one native truncated-prefix regression plus native V2.1/V2.2/V2.3 reader coverage, without main-only lazy/RLE scaffolding.

Validation:

  • cargo fmt --all: passed
  • Targeted lance-encoding/lance-file corruption regression tests: passed (1 + 12 + 1 + 1 + 5 + 6 tests)
  • cargo clippy --all --tests --benches -- -D warnings: passed
  • Rust build, build-no-lock, linux-build, MSRV, clippy, and format checks: passed.
  • Python and cargo-deny failures are baseline exceptions; the standalone create-rc workflow has no needs on these validation workflows, so no release artifact blocker was identified.
  • No manifests, lockfiles, Python extras, or workflows were changed on this branch.
  • Keep Python/dependency maintenance separate from this focused backport.

professor-moody and others added 3 commits August 3, 2026 04:26
…er::unzip (#8138)

`parse_length` reads the length prefix out of the page buffer with
`get_unchecked`, and the only thing between it and the end of the buffer
is a `debug_assert!`:

```rust
// Safety: Data should have at least bytes_per_length bytes remaining
debug_assert!(databuf.len() >= bytes_per_length);
let length = unsafe { Self::parse_length(databuf, in_bits_per_length) };
```

There is no `[profile.release]` override in the workspace `Cargo.toml`,
so `debug-assertions` defaults to false in release and that assertion is
not present in the published wheels.

The loop it sits in continues on `while !databuf.is_empty()`, so it
enters the body with as little as one byte remaining. `parse_length`
then reads up to eight. A page whose item walk ends with a partial
trailing item therefore reads past the end of the buffer.

Reproduced on x86-64 with `-Zsanitizer=address` on a release build,
driving the real `VariableFullZipDecoder::new`:

```
ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 8 at 0x7b9989be1017
  #0 <lance_encoding::...::VariableFullZipDecoder>::new
0x7b9989be1017 is located 3 bytes after 4-byte region [0x...1010,0x...1014)
```

A well-formed control buffer is clean in the same run.

## The change

The payload read one line below the call site is already bounds checked
and panics on malformed input:

```rust
unzipped_data.extend_from_slice(&databuf[..length as usize]);
```

So a truncated item already fails cleanly on the payload path. Only the
length read was inconsistent. This makes the two match by using safe
indexing in `parse_length`, which lets the `unsafe` block and the
`debug_assert!` both go away.

On valid input the behaviour is unchanged. On a truncated trailing item
the result is the same clean panic the payload path already produces,
rather than an out-of-bounds read.

## Tests

Two, per the contributing guide:

- `variable_full_zip_wellformed_length_prefix` decodes a well-formed
prefix
- `variable_full_zip_truncated_length_prefix_is_rejected` is
`#[should_panic]` and covers the case above

Both pass, and the crate's existing suite is unaffected (520 passing
before and after).

## Scope, stated honestly

I have not established that a `.lance` file produced by the writer can
reach this state. Truncating a data file is rejected earlier by the I/O
range check, and a sweep of in-place single-byte edits either read
cleanly, were rejected by that same check, or panicked in safe code
further along in decode. So I am not claiming this is reachable from a
crafted dataset, and I am filing it as hardening rather than as a
security report.

The case for the change does not depend on that: an `unsafe` read whose
only guard is compiled out of release builds is worth removing on its
own, particularly when the adjacent read of the same buffer is already
checked.

## One unrelated observation

Not part of this change, and not something I have shown to be a bug, but
it looked odd while reading. The length is read using
`in_bits_per_length` and the cursor is then advanced by
`bytes_per_offset`, which comes from `out_bits_per_offset`:

```rust
let length = ... parse_length(databuf, in_bits_per_length);
databuf = &databuf[bytes_per_offset..];
```

Those are equal in the common case, so this may well be deliberate.
Flagging it only in case the asymmetry is unintentional.

---------

Co-authored-by: Xuanwo <github@xuanwo.io>
@github-actions github-actions Bot added A-encoding Encoding, IO, file reader/writer bug Something isn't working labels Aug 3, 2026
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 56.42458% with 156 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...st/lance-encoding/src/encodings/physical/binary.rs 48.40% 76 Missing and 5 partials ⚠️
rust/lance-encoding/src/data.rs 67.44% 54 Missing and 2 partials ⚠️
.../lance-encoding/src/encodings/logical/primitive.rs 34.48% 17 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@Xuanwo
Xuanwo marked this pull request as ready for review August 3, 2026 05:41
@Xuanwo
Xuanwo merged commit bfd6d21 into release/v7.1 Aug 3, 2026
31 of 40 checks passed
@Xuanwo
Xuanwo deleted the backport/8138-8144-to-release-v7-1 branch August 3, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-encoding Encoding, IO, file reader/writer bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants