- Type: race condition
- Severity: medium
- Confidence: certain
contrib/puff/puff.c:491
fixed() performed unsynchronized lazy initialization of shared static fixed-Huffman decode tables. When two threads entered puff() on fixed-code blocks at the same time, one thread could consume partially constructed tables via codes()/decode(), causing spurious inflate failure or misdecode.
- Verified from the supplied reproducer and runtime evidence
- Source: local patch
019-fixed-huffman-table-initialization-races-across-threads.patch - Reference: https://swival.dev
- Two threads call
puff()concurrently - Both inputs reach block type
1, which routes throughfixed() - First-use table initialization has not yet completed in one thread before the other starts decoding
The reproducer observed concurrent accesses to fixed.virgin, fixed.lencnt, fixed.lensym, fixed.distcnt, and fixed.distsym from initializer threads, with overlapping reads from codes() at contrib/puff/puff.c:460 and contrib/puff/puff.c:480. In the same run, valid input failed with ret=-11 (distance too far back), which is consistent with a corrupted fixed-Huffman decode path caused by partial table construction.
The fixed-code tables are process-global state used as decoder invariants. Publishing them without synchronization allows another thread to observe virgin and the backing arrays in an intermediate state. That directly breaks correctness: the decoder may reject valid streams, or decode with incorrect symbols/distances. The reproducer demonstrated an actual failure, so this is not a theoretical data-race claim.
Initialization of the fixed Huffman tables must happen exactly once with thread-safe publication semantics, or the tables must be built eagerly before any concurrent decoding can begin.
The patch removes the thread-unsafe lazy-init window by making fixed-table construction occur safely before concurrent use. This ensures no caller can enter codes() with partially initialized lencode or distcode, preserving the decode-table invariant across threads.
None
019-fixed-huffman-table-initialization-races-across-threads.patchguards one-time fixed-table initialization so shared decode tables are fully constructed before publication to concurrentpuff()callers- The change is localized to
contrib/puff/puff.cand addresses the root cause infixed(), rather than the downstream symptom site atcontrib/puff/puff.c:491