Skip to content

Bounds-check unchecked input reads in fastlz1/fastlz2_decompress (OOB read) - #12

Open
eobi wants to merge 1 commit into
ariya:masterfrom
eobi:fix-decompress-oob-read
Open

eobi wants to merge 1 commit into
ariya:masterfrom
eobi:fix-decompress-oob-read

Conversation

@eobi

@eobi eobi commented Sep 7, 2026

Copy link
Copy Markdown

Bound-check the two unchecked input reads in the decompressors

fastlz_decompress() is documented as "crash-proof against corrupted and/or malicious data",
and the decoders enforce that with the always-on FASTLZ_BOUND_CHECK macro. Two input reads are
missing that check, so a crafted/truncated stream reads one byte past the end of the input buffer
(CWE-125). A third read happens before the length is validated.

fastlz2_decompress — the code = *ip++; right after the if (len == 7-1) do { ... } while (code == 255); loop has no bound check (it is the only one of the function's input reads without
one). A 3-byte input 20 ff 38 reaches it with ip at the end of the buffer:

AddressSanitizer: heap-buffer-overflow READ of size 1 ... in fastlz2_decompress fastlz.c:449

fastlz1_decompress — the analogous ref -= *ip++; after its if (len == 7-1) { ...; len += *ip++; } block is likewise unchecked.

fastlz_decompress — reads the level byte *(const uint8_t*)input before checking
length >= 1, so a zero-length call over-reads by one byte.

Fix

Three lines, using the existing FASTLZ_BOUND_CHECK macro (this PR).

Verification

  • Repro (cc -g -fsanitize=address poc.c fastlz.c && ./poc, PoC in the linked report): faults
    before, clean after.
  • Round-trip: 4096 bytes compress → decompress is byte-identical after the fix.
  • Re-fuzzed the patched build ~27M iterations under libFuzzer+ASan: no crash.

Found with a coverage-guided libFuzzer + ASan harness; happy to share it.

tbybell pushed a commit to gtkwave/libfst that referenced this pull request Oct 15, 2026
* Build fastlz without the alignment sanitizer

* Update fastlz to upstream master

Update the vendored copy with `fastlz.c` and `fastlz.h` from the master
branch of https://github.com/ariya/FastLZ at commit b1342da, from August
2024. The file is labeled as version 0.5.0, but that version was first
released in February 2020. The vendored version before this commit was
based on one from 2007.

The new version bounds-checks the input, so it supersedes some of the
checks previously introduced in this vendored copy. The new bounds checks
cover more areas than the previous ones, but they still do not cover
everything (this is fixed in the follow-up commit).

* Bounds-check the two unchecked input reads in the decompressors (CWE-125 OOB read)

Pulled from ariya/FastLZ#12, commit
d29d1abeb1d503db15586788249011ea8a753b70.

---------

Co-authored-by: Obi Ebuka David <davidobi023@gmail.com>

This branch has not been deployed

No deployments
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