as_isopen boolean refcount mismatch causes cross-process memory wipe and device_unbusy on still-open device
Summary
sc->as_isopen is boolean flag not open-count reference counter. Two processes open /dev/agpgart second open skips device_busy. First close frees ALL AGP memory (including second process allocations) releases acquire state clears as_isopen calls device_unbusy even though second process still has device open. Causes silent loss of second process AGP memory and device_busy/device_unbusy count mismatch allowing device detach during active use. If root kldunloads agp.ko detach may proceed (busy count 0) freeing softc while fd retains dangling si_drv1 pointer -> UAF. [VERIFIED pass2: reproduced through verbatim agp open-close - two opens one device_busy, FIRST close frees ALL AGP memory including the second openers allocation and releases acquire while B holds the device; isopen-counting fix-validated exactly-once cleanup]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2399 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 806 B | β raw | |
| VERDICT.md | β | 1.4 KB | β raw | |
| env.txt | β | 145 B | view raw | |
| fix.diff | β | 1.0 KB | view raw | |
| fixed.log | β | 447 B | view raw | |
| run.log | β | 532 B | view raw | |
| run.sh | β | 121 B | view raw | |
| verdict.json | β | 1.9 KB | view raw |
DF-2399 - agp as_isopen boolean vs open refcount
Rig (agp_shim.c test2399: VERBATIM agp_open/agp_close on a fabricated
agp device with a memory allocation and AGP_ACQUIRE_USER state)
after openA: isopen=1 busy=1
after openB: isopen=1 busy=1 <-- second open skipped device_busy
after closeA: isopen=0 busy=0, memory list EMPTY, state released
<-- while B still has it open
after closeB: isopen=0 busy=0 <-- no double-unbusy (guard), but the
mismatch already happened
Fixed kernel (fix.diff: refcounted as_isopen)
after openA: isopen=1 busy=1; after openB: isopen=2 busy=1 after closeA: isopen=1 busy=1, memory list NOT empty, state kept after closeB: isopen=0 busy=0 (cleanup exactly once)
DF-2399 VERDICT
Status: reproduced (reproduced) Impact: corruption Confidence: certain Attempts: 2
Through the VERBATIM agp_open/agp_close: two opens produced ONE device_busy (busy stayed 1 after openB), and the FIRST close freed ALL AGP memory on the list (including the second openers allocation), released the acquire state and called device_unbusy (busy 0, isopen 0) while the second fd still had the device open - cross-process memory wipe plus a busy-count mismatch that lets detach proceed during active use (dangling si_drv1). Second close was a no-op (guard if (sc->as_isopen)). Fixed kernel: isopen counts 2 opens, closeA leaves isopen=1/busy=1 with the memory list INTACT, cleanup only at closeB.
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_testableConfirmed kernel references
Detail
Exploit chain
second process AGP memory silently freed + device detach allowed while an fd holds it -> UAF on the agp softc through /dev/agpgart on real AGP hardware (root-gated device)
Evidence (decisive lines)
run.log (openA busy=1; openB busy=1; closeA isopen=0 busy=0 memory list EMPTY state released while B open); fixed.log (openA/B isopen=2; closeA isopen=1 busy=1 memory NOT empty state kept; closeB isopen=0 busy=0)
PoC changes
agp_shim.c test2399 drives the verbatim cdev open/close entry points with fabricated dev_ops args
Verified recommended fix
see fix.diff
Verdict
Through the VERBATIM agp_open/agp_close: two opens produced ONE device_busy (busy stayed 1 after openB), and the FIRST close freed ALL AGP memory on the list (including the second openers allocation), released the acquire state and called device_unbusy (busy 0, isopen 0) while the second fd still had the device open - cross-process memory wipe plus a busy-count mismatch that lets detach proceed during active use (dangling si_drv1). Second close was a no-op (guard if (sc->as_isopen)). Fixed kernel: isopen counts 2 opens, closeA leaves isopen=1/busy=1 with the memory list INTACT, cleanup only at closeB.
No comments yet.