Files
syft/syft/internal/unionreader
Alex Goodman 04a6fa1b41 fix(unionreader): honor the io.ReaderAt contract in readerAtAdapter (#5186)
`readerAtAdapter.ReadAt` seeks and then issues a single `Read`, which breaks the
`io.ReaderAt` contract in both directions against the squashfs reader it exists
to wrap:

- `squashfs.File.Read` copies against the decompressed block length but advances
  its block cursor by the nominal block size, so a block that decompresses short
  silently stops copying and returns fewer bytes with a nil error. `ReadAt`
  forbids that, and callers rely on it: anything decoding a fixed-size structure
  off the result gets zero padding it has no way to detect and parses it as real
  data. The GraalVM PE export table and the UPX block reader both size a buffer
  from a header field and then ignore `n` entirely, so a crafted image drives
  them straight through the padding.

- a read landing exactly on the end of the file returns a *full* buffer paired
  with `io.EOF`. `bytes.Reader.ReadAt` returns nil there, and the callers that
  treat any error as fatal were written against that, so squashfs-resident
  binaries sized near a read boundary were being skipped outright.

`io.ReadFull` normalizes both: it fills the buffer across short reads, and it
clears the error once the buffer is full. A genuinely short tail is reported as
`io.EOF`, which is what `ReadAt` implementations return at the end of a file, and
what the buffering branch of `GetUnionReader` already returns.

Affects squashfs-backed sources (snaps), so in practice the binary catalogers
reading structure out of executables.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-08-14 14:09:10 -04:00
..