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

Integer overflow in aperture bounds check in agp_generic_bind_memory

Summary

Bounds check offset+mem->am_size > AGP_GET_APERTURE(dev) at :522 can be bypassed via unsigned 64-bit integer wraparound. User-supplied pg_start shifted left 12 bits produces offset near 2^64 causing offset+size to wrap to small value that passes check. Also offset<0 is dead code (vm_offset_t unsigned). Signed left shift past sign bit UB per C11. Currently mitigated by chipset-level bind_page bounds checks in all four chipset drivers but generic code is primary defense and is broken. Same pattern replicated in intel-gtt.c:1259-1260. [VERIFIED pass2: reproduced through verbatim agp.c - pg_start 0xFFFFFFFFFFFFF wrap bind passes the offset-plus-size check via wrap to 0 and invokes chipset bind_page far outside the 64MB aperture; wrap-reject fix-validated A/B]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2398 Β· 8 files
FileTypeDescriptionSize
README.md β€” 879 B ↓ raw
VERDICT.md β€” 1.4 KB ↓ raw
env.txt β€” 145 B view raw
fix.diff β€” 1.0 KB view raw
fixed.log β€” 409 B view raw
run.log β€” 512 B view raw
run.sh β€” 121 B view raw
verdict.json β€” 1.9 KB view raw

DF-2398 - agp bind aperture check integer wrap

Rig (agp_shim.c: VERBATIM agp.c compiled into ACHARNESS with

device agp removed; fabricated 64MB-aperture chipset implementing the real agp_if methods; VM page accessors interposed) - control: agp_generic_bind_memory(dev, mem, 0x8000000) -> EINVAL, 0 calls - wrap: agp_generic_bind_memory(dev, mem, 0xfffffffffffff000) -> PASSES the check (offset+size wraps to 0), chipset bind_page invoked at 0xfffffffffffff000

Baseline (run.log)

control bind offset=2x aperture -> error=22 bind_calls=0
WRAP bind offset=0xfffffffffffff000 am_size=0x1000 -> error=0 bind_calls=1
   bind range=[0xfffffffffffff000..0xfffffffffffff000] aperture=0x4000000
VERDICT: BUG REPRODUCED

Fixed kernel (fix.diff: subtractive bounds form)

WRAP bind offset=0xfffffffffffff000 -> error=22 bind_calls=0
VERDICT.md
↓ download raw

DF-2398 VERDICT

Status: reproduced (reproduced) Impact: corruption Confidence: certain Attempts: 2

agp_generic_bind_memory aperture bounds check bypassed by 64-bit wraparound through the VERBATIM production code: control bind at 2x aperture correctly rejected (EINVAL, 0 chipset calls) while the wrap bind offset=0xfffffffffffff000 am_size=0x1000 (pg_start=0xFFFFFFFFFFFFF) PASSED the offset + mem->am_size > AGP_GET_APERTURE() check (sum wraps to 0) and invoked the chipset bind_page method at offset 0xfffffffffffff000 - far outside the 64MB aperture. The dead offset < 0 test (vm_offset_t unsigned) confirmed. Fixed kernel rejects the wrap with EINVAL, 0 chipset calls.

Kernel references

Guest

DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #7: Sat Oct 24 00:08:04 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/ACHARNESS x86_64

Fix validation

See fix.diff and README.md for the baseline/fixed A/B outputs (run.log / fixed.log).

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
↓ fix.diffper-fix-DF-2398

Confirmed kernel references

Detail

Exploit chain

AGPIOC_BIND pg_start from userspace wraps the check and reaches the chipset driver with an out-of-aperture GATT index; on the four in-tree chipsets their own bind_page checks currently stop it, but any chipset driver relying on the generic check gets out-of-bounds GATT writes

Evidence (decisive lines)

run.log (control error=22 calls=0; WRAP error=0 bind_calls=1 range [0xfffffffffffff000..0xfffffffffffff000]); fixed.log (WRAP error=22 calls=0)

PoC changes

agp_shim.c verbatim-interposition harness (real agp.c compiled in, agp_if methods + VM accessors interposed) written for this batch

Verified recommended fix

see fix.diff

Verdict

agp_generic_bind_memory aperture bounds check bypassed by 64-bit wraparound through the VERBATIM production code: control bind at 2x aperture correctly rejected (EINVAL, 0 chipset calls) while the wrap bind offset=0xfffffffffffff000 am_size=0x1000 (pg_start=0xFFFFFFFFFFFFF) PASSED the offset + mem->am_size > AGP_GET_APERTURE() check (sum wraps to 0) and invoked the chipset bind_page method at offset 0xfffffffffffff000 - far outside the 64MB aperture. The dead offset < 0 test (vm_offset_t unsigned) confirmed. Fixed kernel rejects the wrap with EINVAL, 0 chipset calls.