β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2564

Decompress path signed integer underflow from missing minimum packet size check

Summary

ng_pred1_decompress checks only upper bound on packet size (:468 inlen>PRED1_BUF_SIZE) but no minimum. When compressed frame (cf bit set in 2-byte header) has fewer than 4 bytes total expression inlen-4 at :492 underflows to negative value passed as slen to Pred1Decompress. Pred1Decompress outer loop while(slen) treats negative slen as non-zero running unboundedly bounded only by dlen=4096 reading up to ~514 bytes past inbuf into outbuf within same heap allocation thoroughly corrupting prediction dictionary. Remote PPP peer sends 2-byte compressed frame [0x80 0x00]. Dictionary desync all subsequent frames fail CRC/length checks DoS. [VERIFIED pass2: reproduced - 2-byte frame 80 00 feeds inlen-4 -2 into Pred1Decompress, kernel log shows uint16 of the -1 return proving the full 4096-iteration stale walk; dictionary desync DoS]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2564 Β· 5 files
FileTypeDescriptionSize
harness.c β€” 8.2 KB view raw
build.sh β€” 91 B view raw
worstcase.h β€” 12.8 KB view raw
run.log β€” 1.3 KB view raw
run.2.log β€” 0 B ↓ download

Fix verification

not_testable
↓ fix.diffper-fix-DF-2564

Confirmed kernel references

Detail

Evidence (decisive lines)

pred1_all.log mode 3 + dmesg Comp length error (0) --> len (65535); mode 4 fixed guard rejects

PoC changes

seed frame + 2-byte compressed frame through the real decompress

Verified recommended fix

reject inlen < 4 (minimum = 2 header + 2 FCS) before the compressed branch

Verdict

ng_pred1_decompress checks only the upper bound; a 2-byte compressed frame [0x80,0x00] makes inlen-4 = -2 at :492 and Pred1Decompresss while(slen) loop runs on negative slen until dlen exhaustion. REAL-code run: kernel logged ng_pred1: Comp length error (0) --> len (65535) -- len1=0xFFFF is the uint16_t truncation of the dlen-exhausted return(-1), proving the full 4096-iteration walk fed by bytes past the 2 valid input bytes (stale inbuf content); rc=EIO with stats.Errors incremented. Dictionary desync + reset-request storm for the PPP peer as filed; the walk stays inside the priv allocation (no crash).