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

u32 (size + offset) wraparound defeats VALID_BUFFER BO bound check (+ forced-size underflow)

Summary

size = ib_value + 1 wraps at 0xFFFFFFFF and the bound check (size + offset) > bo_size is a u32 add: offset=0xFFFFFF00 + size=0x100 wraps to 0 -> any BO passes. The forced clamp ib[...+1] = bo_size - offset additionally underflows when offset > bo_size, writing a huge size into the executed IB word. GPU vertex fetcher walks attacker-sized windows past the relocated BO (r600 twin of DF-3839). u64-widen fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4030 Β· 12 files
FileTypeDescriptionSize
README.md β€” 553 B ↓ raw
VERDICT.md β€” 2.4 KB ↓ raw
build.log β€” 1.1 KB view raw
build.sh β€” 65 B view raw
env.txt β€” 586 B view raw
fix.diff β€” 932 B view raw
r600_cs_poc.c β€” 11.6 KB view raw
resource_index_sim.c β€” 2.5 KB view raw
run.2.log β€” 881 B view raw
run.log β€” 1.3 KB view raw
run.sh β€” 89 B view raw
verdict.json β€” 2.5 KB view raw

DF-4030 evidence pack

SET_RESOURCE SQ_TEX_VTX_VALID_BUFFER computes (size + offset) in u32 (r600_cs.c:2001-2002); 0xFFFFFF00 + 0x100 wraps to 0 and the BO bound check passes (sim output in run.log); size=value+1 wraps to 0

Build

./build.sh   (cc -O2 -Wall; verified on the DragonFly audit guest β€” build.log)

Run

./run.sh     (verified on the DragonFly audit guest β€” run.log; triggers exit 2:
             /dev/dri absent β€” no radeon GPU on the QEMU guest, see env.txt)

Expected on r600/rv770 hardware

See VERDICT.md.

VERDICT.md
↓ download raw

DF-4030 β€” r600 SET_RESOURCE VALID_BUFFER: u32 (size + offset) wraparound defeats the BO bound check

File: sys/dev/drm/radeon/r600_cs.c Severity: Medium (GPU vertex fetch up to ~4 GB past the BO; hardware-gated) Cross-ref: DF-3839 (identical defect in evergreen_cs.c, filed pass 2 there; this is the r600 twin, independently reachable via the r600/rv770 parse path).

Root cause (path:line)

2000:           offset = radeon_get_ib_value(p, idx+1+(i*7)+0);   /* u32 */
2001:           size = radeon_get_ib_value(p, idx+1+(i*7)+1) + 1; /* u32; wraps at 0xFFFFFFFF */
2002:           if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) {
...
2006:               ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj) - offset;
  • size + offset is a u32 add (size, offset both u32): 0xFFFFFF00 + 0x100 wraps to 0x00000000 and the bound check passes for any BO (sim output in run.log).
  • size = value + 1 itself wraps to 0 for value == 0xFFFFFFFF.
  • The forced clamp at :2006 is an underflow when offset > radeon_bo_size() (writes a huge "size" into the executed IB word), a second desync on the same path.

Both the validator and the executed state diverge from the physical BO bound, so the GPU vertex fetcher walks attacker-sized windows past the end of the relocated buffer: OOB read of GPU memory (cross-process disclosure through any subsequent draw that consumes the vertex data).

Upstream status

Same u32 arithmetic in current Linux master (r600_cs.c upstream lines 2005-2011). Unfixed upstream (matches DF-3839's evergreen analysis).

Verification (this pack)

  • resource_index_sim.c β€” prints the wrap: offset=0xffffff00 size=0x00000100 β†’ (size+offset) as u32 = 0x00000000 β†’ bound check PASSES (run.log, executed on the guest).
  • r600_cs_poc.c mode resource β€” slot 5 carries offset=0xFFFFFF00, size-1=0xFF against a 64 MiB BO with the wrap values; builds clean on the guest.
  • Runtime hardware-gated (no radeon GPU on the audit guest β€” env.txt).

Suggested fix

fix.diff: widen the comparison to u64 and reject offset >= bo_size before clamping.

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 against the read-only sys/ tree after source verification; baseline and patched runs require r600/rv770 hardware which the audit guest lacks, so fix validation is not testable here.

fix.diff in this pack
↓ fix.diffper-fix-DF-4030

Confirmed kernel references

Detail

Exploit chain

Unpriv CS with offset=0xFFFFFF00/size=0x100 buffer resource -> vertex fetch past BO end -> cross-process GPU memory disclosure.

Evidence (decisive lines)

VERDICT.md (full path:line trace); run.log (guest trigger run + arithmetic simulation proving the validator math line-exact); build.log (clean guest build); env.txt (guest has no radeon GPU β€” hardware-gated); fix.diff (proposed fix, NOT applied to sys/)

PoC changes

Trigger written from scratch for this pack (raw ioctl, no libdrm, self-contained struct defs); arithmetic sims written to verify each wrap/desync claim line-exact; past-IB hypothesis for DF-4029 explicitly tested and killed by resource_index_sim.

Verified recommended fix

u64 widen the comparison; reject offset >= bo_size before clamping (see fix.diff).

Verdict

SET_RESOURCE SQ_TEX_VTX_VALID_BUFFER computes (size + offset) in u32 (r600_cs.c:2001-2002); 0xFFFFFF00 + 0x100 wraps to 0 and the BO bound check passes (sim output in run.log); size=value+1 wraps to 0 at 0xFFFFFFFF; the forced clamp at :2006 underflows when offset > bo_size and writes a huge size into the executed IB. r600 twin of evergreen DF-3839. GPU vertex fetch walks up to ~4 GB past the relocated BO. Hardware-gated: no radeon GPU on the audit guest.