Skip to content

SquashfsHandler: backport 7-Zip 26.01 ReadBlock bounds-check hardening - #262

Open
tonghuaroot wants to merge 1 commit into
p7zip-project:masterfrom
tonghuaroot:fix/squashfs-frag-offset-oob-26.01
Open

SquashfsHandler: backport 7-Zip 26.01 ReadBlock bounds-check hardening#262
tonghuaroot wants to merge 1 commit into
p7zip-project:masterfrom
tonghuaroot:fix/squashfs-frag-offset-oob-26.01

Conversation

@tonghuaroot

Copy link
Copy Markdown

Backports two CHandler::ReadBlock() hardenings landed in upstream 7-Zip 26.01 (released 2026-04-27).

What changed upstream

  1. The bound just before the final memcpy(dest, _cachedBlock + offsetInBlock, blockSize) was

    ```cpp
    if (offsetInBlock + blockSize > _cachedUnpackBlockSize)
    return S_FALSE;
    ```

    which is overflow-prone if offsetInBlock + blockSize wraps a 32-bit add. 26.01 rewrites it in overflow-safe subtractive form:

    ```cpp
    if (_cachedUnpackBlockSize < offsetInBlock ||
    _cachedUnpackBlockSize - offsetInBlock < blockSize)
    return S_FALSE;
    ```

  2. For fragmented file inodes, offsetInBlock = node.Offset is taken directly from inode metadata. 26.01 adds a sanity check offsetInBlock <= _h.BlockSize immediately afterwards so an inode that claims an offset beyond a block is rejected up-front.

Impact

A crafted SquashFS image with attacker-controlled node.Offset near 0xFFFFFFFF combined with a small blockSize can wrap offsetInBlock + blockSize below _cachedUnpackBlockSize and pass the check, after which the memcpy performs a length-controlled OOB read past _cachedBlock and writes the bytes into the caller's dest buffer.

Patch

Two small hunks, byte-for-byte matching upstream 26.01.

Disclosure

I am not a native English speaker. AI tooling was used to polish the prose in this PR description. The fix itself is a direct backport from upstream 7-Zip 26.01 source; no design choices were made beyond matching upstream verbatim.

Upstream 7-Zip 26.01 (released 2026-04-27) hardened
CHandler::ReadBlock() against two attacker-influenced inputs in
malformed SquashFS images:

1. The bounds check before the final memcpy was

       if (offsetInBlock + blockSize > _cachedUnpackBlockSize)

   which can wrap when offsetInBlock + blockSize overflows UInt32.
   26.01 rewrites it in overflow-safe subtractive form:

       if (_cachedUnpackBlockSize < offsetInBlock ||
           _cachedUnpackBlockSize - offsetInBlock < blockSize)

2. For fragmented file inodes, `offsetInBlock = node.Offset` is taken
   directly from inode metadata. 26.01 adds a sanity check that
   `offsetInBlock <= _h.BlockSize` before proceeding, so an inode
   that claims an offset beyond the block size is rejected up-front.

Patch matches upstream 7-Zip 26.01 source.

Signed-off-by: tonghuaroot <tonghuaroot@gmail.com>
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