Summary
Two related bugs in aceapex 1.0, both triggered by small inputs:
- Heap corruption / abort. Compressing many small buffers in sequence aborts with
free(): double free detected in tcache 2 (SIGABRT).
- Incorrect result on tiny inputs. For some very small sizes, the decode round-trip fails —
aceapex_decompress returns -1 and the output does not match the input.
Found while benchmarking aceapex in lzbench.
Environment
- aceapex 1.0
- lzbench 2.2.1
- GCC 13.3.0, x86-64 Linux (AMD EPYC 9554)
Reproduce — double-free
Run, from a checked-out lzbench tree (which contains thousands of files of varied/tiny sizes), aceapex over every file:
./lzbench -eaceapex -v10 -t0,0 -jr .
It aborts mid-compression after a couple thousand small chunks:
ENC chunk=2154/5915 part=7605 clen=7867 out=37019509->33317524
ENC chunk=2155/5915 part=356 free(): double free detected in tcache 2
Aborted (exit 134)
part=NNN is the per-buffer input size in bytes; the abort happens while compressing a small (~356-byte) buffer after ~2154 prior compress/decompress calls. It's not a single bad file — it's the repeated-call path corrupting the heap. (Running under valgrind/ASan should pinpoint the offending free.)
Reproduce — minimal, wrong result on tiny input
printf x > one # 1 byte
./lzbench -eaceapex -t0,0 one
ERROR in aceapex: decompressed size mismatch in_bytes[1] != out_bytes[-1]
i.e. aceapex_decompress(...) returns -1 for the 1-byte input (compress produced 175 bytes, decode then fails). Scanning sizes 0–10 bytes, the failing ones are 1, 2, and 5 bytes; 0, 3, 4, 6–10 round-trip fine. Larger single buffers (tested up to 128 MB) are also fine. So the bug is specific to certain very small input sizes / block edge cases.
Likely area
The block/stream handling for inputs smaller than (or near) a single block looks like the culprit — both the -1 decode failures and the eventual double-free point at small-buffer edge cases in encode_file / the stream alloc-free path. Thanks for taking a look!
Summary
Two related bugs in aceapex 1.0, both triggered by small inputs:
free(): double free detected in tcache 2(SIGABRT).aceapex_decompressreturns-1and the output does not match the input.Found while benchmarking aceapex in lzbench.
Environment
Reproduce — double-free
Run, from a checked-out lzbench tree (which contains thousands of files of varied/tiny sizes), aceapex over every file:
It aborts mid-compression after a couple thousand small chunks:
part=NNNis the per-buffer input size in bytes; the abort happens while compressing a small (~356-byte) buffer after ~2154 prior compress/decompress calls. It's not a single bad file — it's the repeated-call path corrupting the heap. (Running under valgrind/ASan should pinpoint the offendingfree.)Reproduce — minimal, wrong result on tiny input
i.e.
aceapex_decompress(...)returns-1for the 1-byte input (compress produced 175 bytes, decode then fails). Scanning sizes 0–10 bytes, the failing ones are 1, 2, and 5 bytes; 0, 3, 4, 6–10 round-trip fine. Larger single buffers (tested up to 128 MB) are also fine. So the bug is specific to certain very small input sizes / block edge cases.Likely area
The block/stream handling for inputs smaller than (or near) a single block looks like the culprit — both the
-1decode failures and the eventual double-free point at small-buffer edge cases inencode_file/ the stream alloc-free path. Thanks for taking a look!