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

r100_packet3_check() reads payload dwords at fixed offsets without validating pkt->count -> OOB read 1 dword past the IB chunk end + kernel-log disclosure (same class as DF-1492/DF-3592)

Summary

radeon_cs_packet_parse() only guarantees pkt->idx + count + 1 < length_dw and radeon_get_ib_value() is unchecked. The DRAW handlers unconditionally read ib[pkt->idx+2] and the 0x23 handler reads up to ib[pkt->idx+4] regardless of count. A truncated (count=0) packet placed as the last 2 dwords of the IB reads one dword past the chunk: on AGP past the kvmalloc'd kdata array, on PCI/PCIe past the exact-size sa_bo sub-allocation into the shared IB pool (stale/other-process GPU commands). The OOB dword becomes track->vap_vf_cntl and is printed to the world-readable kernel log as 'VAP_VF_CNTL.NUM_VERTICES %u' - a repeatable 16-bit kernel-heap/GPU-pool disclosure oracle for a local DRI user. Class-equal to DF-1492 (r300)/DF-3592 (si); identical unguarded reads exist upstream.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3764 Β· 10 files
FileTypeDescriptionSize
df3764_oob_read.c β€” 3.2 KB view raw
build.sh β€” 104 B view raw
run.sh β€” 118 B view raw
build.log β€” 221 B view raw
run.log β€” 266 B view raw
env.txt β€” 595 B view raw
fix.diff β€” 1.3 KB view raw
VERDICT.md β€” 3.0 KB ↓ raw
manifest.json β€” 1.2 KB view raw
verdict.json β€” 3.5 KB view raw
VERDICT.md
↓ download raw

DF-3764 - VERDICT

status: inconclusive (runtime) | reproduced: 0 | impact (expected): leak | confidence: certain (source)

What was verified

r100_packet3_check() unconditionally reads payload dwords at fixed offsets (idx+1 for DRAW_VBUF/DRAW_INDX/DRAW_IMMD, up to idx+3 for the 0x23 packet) while radeon_cs_packet_parse() only validates that the packet header+count fit: pkt->idx + count + 1 < length_dw (radeon_cs.c:768). A truncated packet (count=0) placed as the last 2 dwords of the IB therefore makes the handler read ib[length_dw] - one dword past the IB chunk. radeon_get_ib_value() (radeon.h:1098) has no bounds check. On AGP systems the IB chunk is a kvmalloc'd array of exactly length_dw dwords (radeon_cs.c:337-346) -> 4-byte kernel heap OOB read; on PCI/PCIe it reads past the radeon_sa_bo sub-allocation (exactly length_dw*4, radeon_ib.c:60) into the shared IB pool (stale/other-process GPU commands). The OOB dword becomes track->vap_vf_cntl and, on the IMMD mismatch path, is printed to the kernel log as 'VAP_VF_CNTL.NUM_VERTICES %u' (r100.c:2364-2367) - dmesg is world-readable, giving a repeated 16-bit heap-disclosure oracle. Same class as DF-1492 (r300) and DF-3592 (si). NOT runtime-verifiable on this guest (no ATI ASIC / radeonkms): inconclusive at runtime, certain at source level.

Kernel references

Why not reproduced on the guest

Guest = DragonFly 6.5-DEVELOPMENT x86_64 under QEMU (virtio-only): - kldstat: only kernel + ehci.ko + xhci.ko (drm.ko exists in /boot/kernel but is not loaded and there is no radeonkms.ko anywhere) - /dev/dri does not exist - pciconf shows no vendor-1002 (ATI) device => The radeon CS ioctl path cannot execute. The finding is proven by line-exact source analysis (integer identities / NULL flow), and the PoC is proven to build (build.log, cc 8.3 -Wall -O2, zero warnings).

Upstream comparison

Checked against Linux torvalds master r100.c (fetched 2026-09-16): identical unvalidated fixed-offset reads present upstream (equivalent; class matches DF-1492/DF-3592).

Fix

fix.diff in this directory (not applied to the read-only sys/ tree; kernel rebuild validation not possible - no target ASIC).

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored from line-exact source analysis; kernel-rebuild validation impossible on this guest (no target ASIC to baseline)

fix.diff in pack
↓ fix.diffper-fix-DF-3764

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (full narrative); df3764_oob_read.c (trigger); build.log (clean compile, cc 8.3 -Wall -O2); run.log (device absent); env.txt (guest has no radeon hw/module); fix.diff

PoC changes

Re-seeded PoC from scratch: DF-style _IOWR('d', nr, type) ioctl encoding (uapi drm.h:800), self-contained uapi structs, packet builders verified against r100d.h/radeon_reg.h constants, register reachability verified against r100_reg_safe.h bitmap (102 entries).

Verified recommended fix

Require minimum packet counts (>=1 for DRAW_VBUF/DRAW_INDX/DRAW_IMMD, >=4 for 0x23) before fixed-offset payload reads

Verdict

r100_packet3_check() unconditionally reads payload dwords at fixed offsets (idx+1 for DRAW_VBUF/DRAW_INDX/DRAW_IMMD, up to idx+3 for the 0x23 packet) while radeon_cs_packet_parse() only validates that the packet header+count fit: pkt->idx + count + 1 < length_dw (radeon_cs.c:768). A truncated packet (count=0) placed as the last 2 dwords of the IB therefore makes the handler read ib[length_dw] - one dword past the IB chunk. radeon_get_ib_value() (radeon.h:1098) has no bounds check. On AGP systems the IB chunk is a kvmalloc'd array of exactly length_dw dwords (radeon_cs.c:337-346) -> 4-byte kernel heap OOB read; on PCI/PCIe it reads past the radeon_sa_bo sub-allocation (exactly length_dw*4, radeon_ib.c:60) into the shared IB pool (stale/other-process GPU commands). The OOB dword becomes track->vap_vf_cntl and, on the IMMD mismatch path, is printed to the kernel log as 'VAP_VF_CNTL.NUM_VERTICES %u' (r100.c:2364-2367) - dmesg is world-readable, giving a repeated 16-bit heap-disclosure oracle. Same class as DF-1492 (r300) and DF-3592 (si). NOT runtime-verifiable on this guest (no ATI ASIC / radeonkms): inconclusive at runtime, certain at source level.