# DF-5328 VERDICT

## Status: NOT REPRODUCED (bounded guest attempt; code-proven race, confidence "likely")

## The claim
`cam_periph_alloc()` (sys/bus/cam/cam_periph.c:176-190) uses the
borrowed, unlocked `cam_periph_find()` result:

```c
periph = cam_periph_find(path, name);      /* buses lock dropped inside */
if (periph != NULL) {
    if ((periph->flags & CAM_PERIPH_INVALID) != 0
     && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) {
        periph->flags |= CAM_PERIPH_NEW_DEV_FOUND;   /* unlocked WRITE */
        periph->deferred_callback = ac_callback;     /* unlocked WRITE */
        periph->deferred_ac = code;
        return (CAM_REQ_INPROG);
```

`camperiphfree()` (cam_periph.c:581-632, from `cam_periph_release()`
:368-377 or `cam_periph_invalidate()` :573-578) removes the periph from
the units list and ultimately `kfree(periph)` at :630. Two races:

(a) **UAF write**: the last release completes camperiphfree between
    alloc's `find()` return and its flag store -> the three stores land
    in freed memory (M_CAMPERIPH, 144 bytes).
(b) **NULL/garbage indirect call**: `camperiphfree()` reads
    `CAM_PERIPH_NEW_DEV_FOUND` at :604 under the buses lock, but the
    flag was stored BEFORE `deferred_callback` in alloc's unlocked
    sequence -> camperiphfree can see flag=1 with callback still NULL
    -> `periph->deferred_callback(NULL, ...)` at :625 -> panic/garbage
    call.

## Why the guest attempt could not hit it
The interleaving window is the handful of instructions between
`find()`'s unlock-return and the first flag store on the allocating
thread, and the freeing thread must complete TAILQ_REMOVE + dtor +
kfree inside it. Precondition: a same-driver INVALID periph with a live
last reference (open fd / in-flight probe) on the exact path being
rescanned.

Rig attempt (`race_alloc.sh`, race_fdchurn.c): ~200
invalidate+rescan cycles interleaved with ~4000 open/close churns on
/dev/ch0 across 90 s on the vulnerable kernel. No crash, no anomalies,
refcount stable at 0 at quiesce. Expected hit rate per cycle is far
below 1e-3; the bounded attempt is documented as evidence of diligence,
not as refutation — the code path stands on the lock-domain analysis
above (FreeBSD restructured this same area; the borrowed-pointer
pattern is the defect).

## Reproduce attempt
```
cd /root/df5327 && sh race_alloc.sh   # ~90 s bounded loop
```
(rig sysctls debug.evilcd.invalidate / .rescan drive create/destroy)

## Fix
`fix.diff`: perform the lookup and the deferred bookkeeping under one
`xpt_lock_buses()` hold (serializing with camperiphfree's
TAILQ_REMOVE/flag read) and store `deferred_callback`/`deferred_ac`
BEFORE setting `CAM_PERIPH_NEW_DEV_FOUND`. Not kernel-rebuilt (race not
reproduced; patch is advisory from the analysis).

## Guest
DragonFly dfbsd 6.5-DEVELOPMENT (EVIL kernel), evilcd2.ko rig.

## 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.
