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

isofncmp ';version' parse: signed int overflow (UB) enables false-positive filename matches via modular wrap

Summary

The ISO9660 version-number parser accumulates digits into a signed int on both sides: user-supplied at :118 (i = i*10 + *fn++ - '0') and image-supplied at :123 (j = j*10 + c - '0'), then returns i - j at :127. Version fields >=10 digits (e.g. 'NAME;4294967297') overflow int (UB, wraps mod 2^32) so lookup('NAME;4294967297') compares i==1 and matches an image entry 'NAME;1'. isofncmp returns 0 (exact match) for unequal names; cd9660_lookup.c:233 treats 0 as exact. Image-side loop also performs no digit validation (c is an arbitrary u_short). Honest ceiling: LOW/none as a boundary crossing - iso9660 is read-only/ACL-less, a false-positive match only resolves an entry the requester could address by its true name; no memory corruption (stack locals). UB noise, ISO9660 semantics divergence, theoretical confusion vs programs treating 'X;N' as distinct. Fix: clamp accumulators + validate image-side digits. [VERIFIED pass2: reproduced - crafted plain-ISO stat NAME 4294967297 matches NAME 1 on stock via 2 to the 32 plus 1 wrapping the int accumulator; F40FIX ENOENT with NAME 1 unchanged]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3246 Β· 7 files
FileTypeDescriptionSize
README.md β€” 235 B ↓ raw
df3246.c β€” 1.0 KB view raw
fix.diff β€” 1.2 KB view raw
manifest.json β€” 503 B view raw
mkiso3246.py β€” 1.8 KB view raw
run.log β€” 445 B view raw
verdict.json β€” 2.0 KB view raw

DF-3246 evidence pack

What

isofncmp ';version' signed overflow enables false-positive matches

Verdict

status=reproduced impact=none confidence=certain

See VERDICT.md for the full narrative and fix.diff for the verified fix.

Fix verification

fixed

A/B complete: NAME;4294967297 now ENOENT on F40FIX (was a false-positive match on stock); NAME;1 still resolves (no regression).

↓ fix.diffper-fix-DF-3246

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md with path:line citations; run.log where a live demo exists; fix.diff

PoC changes

image generator written (host python): PVD@16/VDST@17/root@32/data@33; first two generator attempts had wrong root-record offset (must be PVD byte 156) and wrong dirrec length byte - fixed

Verified recommended fix

refuse version fields whose accumulation would overflow int (both user and image sides)

Verdict

Reproduced on the stock INVARICTIONS guest with a minimal crafted plain ISO9660 image (no RRIP/Joliet) holding one file stored as NAME;1: stat(/mnt/NAME;4294967297) SUCCEEDS (returns the inode of NAME;1) while the controls behave correctly - NAME;2 -> ENOENT, NAME;4294967298 (wraps to 2) -> ENOENT, NAME;0000000001 (10-digit but i==1) matches legitimately. 4294967297 = 2^32+1 wraps the int accumulator at cd9660_util.c:118 (i = i10 + fn++ - 0) to 1, matching the image-side j=1 via return i - j (:127); cd9660_lookup.c:233 treats 0 as an exact match. Signed overflow is UB; on this compiler it wraps. Post-fix A/B on F40FIX: NAME;4294967297 -> ENOENT (version field refused as too large).