-
Notifications
You must be signed in to change notification settings - Fork 103
Description
Summary
sinfl_decompress() has a stack buffer overflow in the dynamic Huffman block parsing where repeat codes (symbols 16/17/18) can write past the 320-byte lens stack buffer. Additionally, the input bitstream is never bounds-checked, and back-reference distances are not validated.
Vulnerability Details
Bug 1: Stack buffer overflow via repeat code lengths (Critical, CWE-121)
In the dynamic Huffman code length parsing (lines 455-463):
unsigned char lens[288+32]; // 320 bytes on stack
for (n = 0; n < nlit + ndist;) {
sym = sinfl_decode(&s, hlens, 7);
switch (sym) {
default: lens[n++] = (unsigned char)sym; break;
case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break;
case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break;
case 18: for (i=11+sinfl_get(&s,7);i;i--,n++) lens[n]=0; break;
}
}The outer loop checks n < nlit + ndist (max 320) only at the top. The inner repeat loops (cases 16/17/18) increment n without checking the bound:
- Case 18 can repeat up to 11 + 127 = 138 times
- If
n = 300and case 18 fires with max repeat:ngoes to 438, writinglens[300]throughlens[437] - This overflows 117 bytes past the 320-byte
lensstack buffer
A crafted DEFLATE stream with a dynamic Huffman block specifying excessive repeat codes triggers this attacker-controlled stack buffer overflow. Case 16 (lens[n]=lens[n-1]) also causes OOB reads of the already-overflowed data.
Bug 2: Input buffer over-read in sinfl_refill (CWE-125)
static void sinfl_refill(struct sinfl *s) {
s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; // reads 8 bytes unconditionally
s->bitptr += (63 - s->bitcnt) >> 3;
s->bitcnt |= 56;
}sinfl_read64 does memcpy(&n, p, 8) — always reads 8 bytes from bitptr. The input end e = in + size is computed at line 385 but is never checked against bitptr in the compressed block code paths. For any input shorter than ~8 bytes of remaining data after the last refill, subsequent refills read past the input buffer into adjacent heap memory.
Bug 3: Back-reference distance not validated (CWE-125)
int offs = sinfl__get(&s, dbits[dsym]) + dbase[dsym];
unsigned char *src = out - offs; // no check that offs <= (out - output_start)A crafted stream can specify a distance larger than the number of bytes already output. src = out - offs then points before the output buffer, causing an OOB read. The output bounds (out > oe) are checked, but there's no validation that the match source is within the already-written output.
Impact
- Bug 1: Attacker-controlled stack buffer overflow up to ~138 bytes. With a crafted DEFLATE stream, this can overwrite the return address and achieve code execution.
- Bug 2: Heap information disclosure — up to 7 bytes of heap data read past the input allocation.
- Bug 3: OOB read from before the output buffer, potential info leak.
Suggested Fix
For Bug 1, clamp the inner repeat loops:
case 16: for (i=3+sinfl_get(&s,2); i && n < nlit+ndist; i--,n++) lens[n]=lens[n-1]; break;
case 17: for (i=3+sinfl_get(&s,3); i && n < nlit+ndist; i--,n++) lens[n]=0; break;
case 18: for (i=11+sinfl_get(&s,7); i && n < nlit+ndist; i--,n++) lens[n]=0; break;For Bug 2, check bitptr against input end before refilling, or document the 8-byte overread requirement.
For Bug 3, add: if (offs > (int)(out - o)) return (int)(out-o);
Found via manual code audit.