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

Texture mipmap BO bounds check is commented out (GPU OOB mip walk past mipmap BO)

Summary

r600_check_texture_resource computes mipmap_size via r600_texture_size and then does nothing: the entire bounds-check body is commented out. Any LAST_LEVEL/BASE_LEVEL/mip-offset combination is accepted regardless of mipmap BO size (the l0_size check covers only level-0). texmip_sim: 512x512 R8, 10 levels vs a 256-byte mipmap BO -> sampler walks 87,125 bytes past the BO. Cross-process GPU disclosure. Restore-check-with-u64 fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4032 Β· 12 files
FileTypeDescriptionSize
README.md β€” 553 B ↓ raw
VERDICT.md β€” 2.7 KB ↓ raw
build.log β€” 1.1 KB view raw
build.sh β€” 65 B view raw
env.txt β€” 586 B view raw
fix.diff β€” 667 B view raw
r600_cs_poc.c β€” 11.6 KB view raw
run.2.log β€” 881 B view raw
run.log β€” 812 B view raw
run.sh β€” 89 B view raw
texmip_sim.c β€” 1.9 KB view raw
verdict.json β€” 2.5 KB view raw

DF-4032 evidence pack

r600_check_texture_resource's mipmap BO bounds check is commented out (r600_cs.c:1603-1606): any LAST_LEVEL/BASE_LEVEL/word3 mip offset is accepted regardless of mipmap BO size. texmip_sim (run.log) s

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-4032 β€” r600 texture mipmap BO bounds check is commented out (GPU OOB mip walk)

File: sys/dev/drm/radeon/r600_cs.c Severity: Medium (cross-process GPU memory disclosure via texture sampling; hardware-gated)

Root cause (path:line)

r600_check_texture_resource() computes the mipmap chain size with r600_texture_size() (r600_cs.c:1399-1457) and then... does nothing:

1602:   /* using get ib will give us the offset into the mipmap bo */
1603:   if ((mipmap_size + word3) > radeon_bo_size(mipmap)) {
1604:       /*dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n",
1605:         w0, h0, format, blevel, nlevels, word3, mipmap_size, radeon_bo_size(texture));*/
1606:   }
1607:   return 0;

The check body is commented out, so any texture resource is accepted regardless of the mipmap BO size. word3 (the user's mip offset) and mipmap_size (derived from LAST_LEVEL/BASE_LEVEL/width/height/format) are otherwise unbounded (the l0_size check at :1594 covers only level-0 in the texture BO).

The GPU texture unit then walks mip levels past the end of the mipmap BO: sampling the texture in a subsequent draw reads adjacent GPU memory (other processes' BOs / GTT pages), and the sampled values can be rendered into an attacker-owned target β€” a cross-process info-leak. The PoC geometry (512x512 R8, 10 levels, 256-byte mipmap BO) walks 87,125 bytes past the BO (texmip_sim, run.log).

The size accumulation itself is also u32 (offset += size in r600_texture_size:1450), so even a restored check must be u64-safe β€” the proposed fix in fix.diff restores it with (u64) casts.

Upstream status

The commented-out check is identical in current Linux master (upstream r600_cs.c:1603-1609 β€” "don't break userspace" heritage). Unfixed upstream.

Verification (this pack)

  • texmip_sim.c β€” replicates r600_mip_minify/r600_texture_size for the PoC resource and prints the per-level offsets: mipmap_size 87,381 bytes vs a 256-byte mipmap BO (run.log, executed on the guest).
  • r600_cs_poc.c mode texmip β€” SET_RESOURCE VALID_TEXTURE submission with the two relocs (texture BO 1 MiB, mipmap BO 256 B) exactly as the parser expects; builds clean on the guest.
  • Runtime hardware-gated (no radeon GPU on the audit guest β€” env.txt).

Suggested fix

fix.diff: restore the check with 64-bit arithmetic and reject oversized mipmap requirements.

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-4032

Confirmed kernel references

Detail

Exploit chain

Unpriv CS with 512x512 R8 texture, 10 mip levels, 256-byte mipmap BO -> sampler reads OOB GPU memory -> draw renders leaked texels into attacker BO.

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

Restore the check with u64 arithmetic (see fix.diff).

Verdict

r600_check_texture_resource's mipmap BO bounds check is commented out (r600_cs.c:1603-1606): any LAST_LEVEL/BASE_LEVEL/word3 mip offset is accepted regardless of mipmap BO size. texmip_sim (run.log) shows the PoC resource walks 87,125 bytes past a 256-byte mipmap BO -> GPU texture sampler reads adjacent GPU memory -> cross-process info leak via rendered samples. Hardware-gated: no radeon GPU on the audit guest.