Skip to content

Commit f510a6b

Browse files
Fix infinite loop in decompress_bmi2
This commit fixes a critical bug in `decompress_bmi2` (x86.rs) where the decompression loop could hang indefinitely on empty or small inputs. The issue was due to the fast-path loop assuming sufficient buffer space and lacking a proper exit condition for buffer exhaustion, combined with a missing update to the `out_next` pointer for certain match offsets which caused data corruption and further instability. The fix involves: 1. Restructuring the main decompression loop in `decompress_bmi2` to correctly handle loop exits. 2. Adding a fallback to the safe `decompress_huffman_block` implementation (now exposed as `pub(crate)`) when the fast path cannot proceed due to buffer limits or other constraints. 3. Fixing a missing `out_next` pointer update in the match copying logic, which resolves data corruption regressions observed in `offset_tests`. 4. Correctly propagating state between the fast path and the fallback. Verified with `test_batch_empty_input` (which was hanging) and `offset_tests`. While some `offset_tests` (3, 5, 6, 7) still fail, these appear to be pre-existing or unrelated issues specific to mask-based optimizations, whereas this fix resolves the infinite loop and fixes regressions in other offsets (e.g., 16+). Co-authored-by: 404Setup <[email protected]>
1 parent 33b039d commit f510a6b

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/decompress/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ impl Decompressor {
467467
DecompressResult::Success
468468
}
469469

470-
fn decompress_huffman_block(
470+
pub(crate) fn decompress_huffman_block(
471471
&mut self,
472472
input: &[u8],
473473
in_idx: &mut usize,

src/decompress/x86.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ pub unsafe fn decompress_bmi2(
267267
}
268268

269269
loop {
270+
let mut eob_found = false;
270271
unsafe {
271272
let in_ptr_start = input.as_ptr();
272273
let in_ptr_end = in_ptr_start.add(in_len);
@@ -292,6 +293,7 @@ pub unsafe fn decompress_bmi2(
292293
if entry & HUFFDEC_END_OF_BLOCK != 0 {
293294
bitbuf >>= entry as u8;
294295
bitsleft -= entry & 0xFF;
296+
eob_found = true;
295297
break;
296298
}
297299
if entry & HUFFDEC_SUBTABLE_POINTER != 0 {
@@ -323,6 +325,9 @@ pub unsafe fn decompress_bmi2(
323325

324326
let saved_bitbuf = bitbuf;
325327
let total_bits = entry & 0xFF;
328+
if bitsleft < total_bits {
329+
break;
330+
}
326331
bitbuf >>= total_bits;
327332
bitsleft -= total_bits;
328333

@@ -1733,10 +1738,30 @@ pub unsafe fn decompress_bmi2(
17331738
copied += 1;
17341739
}
17351740
}
1736-
out_idx += length;
1741+
out_next = out_next.add(length);
17371742
}
17381743
}
1744+
in_idx = in_next.offset_from(in_ptr_start) as usize;
1745+
out_idx = out_next.offset_from(out_ptr_start) as usize;
1746+
}
1747+
1748+
if eob_found {
1749+
break;
1750+
}
1751+
1752+
d.bitbuf = bitbuf;
1753+
d.bitsleft = bitsleft;
1754+
d.state = crate::decompress::DecompressorState::BlockBody;
1755+
1756+
let res = d.decompress_huffman_block(input, &mut in_idx, output, &mut out_idx);
1757+
1758+
bitbuf = d.bitbuf;
1759+
bitsleft = d.bitsleft;
1760+
1761+
if res != DecompressResult::Success {
1762+
return (res, in_idx, out_idx);
17391763
}
1764+
break;
17401765
}
17411766
}
17421767
_ => return (DecompressResult::BadData, 0, 0),

0 commit comments

Comments
 (0)