Skip to content

Fix CVE-2018-12913 regression: guard against code_len==0 infinite loop in tinfl_decompress - #370

Merged
uroni merged 1 commit into
richgel999:masterfrom
000TY:fix/cve-2018-12913-infinite-loop
Feb 13, 2026
Merged

Fix CVE-2018-12913 regression: guard against code_len==0 infinite loop in tinfl_decompress#370
uroni merged 1 commit into
richgel999:masterfrom
000TY:fix/cve-2018-12913-infinite-loop

Conversation

@000TY

@000TY 000TY commented Feb 6, 2026

Copy link
Copy Markdown
Contributor
### Summary

The fix for [CVE-2018-12913](https://nvd.nist.gov/vuln/detail/CVE-2018-12913) (infinite loop in `tinfl_decompress`) was added in commit e680017 but subsequently reverted in commit ae07a36 as part of a formatting revert. This PR re-introduces the fix using a more precise guard condition.

### Problem

In the fast decode path of `tinfl_decompress()`, when a corrupted Huffman table contains an uninitialized entry (value 0), the lookup produces `code_len = 0 >> 9 = 0`. Since zero bits are consumed from the bit buffer on each iteration, the decompressor loops indefinitely, producing ~300 MB/s of zero-filled output. In streaming mode, this is an infinite loop consuming 100% CPU.

This affects:
- `tinfl_decompress()` in streaming mode
- `mz_zip_reader_extract_to_callback()`
- `mz_zip_reader_extract_to_file()`
- `mz_zip_validate_archive()`

### Fix

Add `code_len == 0` checks after both symbol decodes in the fast path loop. The minimum valid Huffman code length is 1, so `code_len == 0` unambiguously indicates an invalid (uninitialized) table entry.

**Why `code_len == 0` instead of the original `sym2 == 0 && counter == 0`:**

The original fix from e680017 checked `sym2 == 0 && counter == 0`, but this has false positives. When both symbols are literal NULL (0x00) decoded via the tree-walk path (where `code_len > TINFL_FAST_LOOKUP_BITS`, perfectly valid), the condition incorrectly triggers. Checking `code_len == 0` is more precise — it only fires on genuinely uninitialized lookup table entries, since valid entries are stored as `(code_size << 9) | symbol` with `code_size >= 1`.

### Testing

- **PoC (streaming)**: Terminates immediately with `TINFL_STATUS_FAILED` (before fix: infinite loop)
- **PoC (ZIP callback)**: Terminates immediately (before fix: infinite loop)
- **Valid data roundtrip** (7 patterns including 1MB of 0x00, both streaming and non-streaming): All 13 tests pass, no false positives
- **libFuzzer + ASan/UBSan** (30 seconds, 102K runs): No crashes or sanitizer violations

### References

- [CVE-2018-12913 (NVD)](https://nvd.nist.gov/vuln/detail/CVE-2018-12913)
- [Original PoC](https://github.com/Edward-L/my-cve-list/blob/master/miniz/README.md)
- [Original fix commit e680017](https://github.com/richgel999/miniz/commit/e680017) (reverted)
- [Revert commit ae07a36](https://github.com/richgel999/miniz/commit/ae07a36)

@uroni
uroni merged commit 4b9fcf1 into richgel999:master Feb 13, 2026
2 checks passed
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.

2 participants