# DF-3193 — VERDICT

**status: not_reproduced (panic) / trigger chain + use-after-free access proven by construction / impact observed: none (benign recycle)**

## What was run (baseline, stock INVARIANTS kernel #0)

Two runs of `./uaf` (6 cycles with zeroed-fmp spray; 5 cycles without
spray). Decisive single-cycle timeline (run.log + serial_full.txt):

1. `[888] daemon: WITHHOLDING reply for FUSE_STATFS unique=3` — worker
   (pid 890) now sleeps in `fuse_ipc_tx()` on its fip.
2. `[887] umount -f /mnt/dfc ... umount OK` — forced umount completes in
   ~1 s *while the worker sleeps* (dounmount forcing path,
   vfs_syscalls.c:975-993; fuse_unmount never aborts/drains in-flight
   IPCs).
3. `[888] daemon: FUSE_DESTROY -> reply, exit, close(fd)` — fd close →
   `fuse_cdevpriv_dtor` → `fuse_mount_free` drops the final ref →
   **fmp kfree'd** (~1.5 s into the cycle).
4. ~35 s later: serial console `fuse_ipc_wait(uaf|891): timeout` /
   `(uaf|892): timeout` — the workers take the full 6×5 s retry path;
   at its end `fuse_ipc_remove()` executed `mtx_lock(&fmp->ipc_lock)`
   and walked `fmp->request_head`/`reply_head` **on the fmp freed in
   step 3** (fuse_ipc.c:125-140). Workers return errno 16
   ("returned through freed-fmp path" in run.log).

The trigger schedule (hung-daemon statfs vs forced umount vs daemon
exit) is deterministic and reproduced in both runs; the freed-memory
lock/walk follows necessarily from the executed path (the "timeout"
print sits immediately before `fuse_ipc_remove` in fuse_ipc.c:185-187).

## Why no crash

The freed ~256-byte M_TEMP block was recycled with benign content
(zeroed fresh fmp objects from the spray run; empty stale heads in the
no-spray run), so the stale `mtx_lock` succeeded and the
`TAILQ_FOREACH` compared against NULL heads and exited. Turning the
walk into a spin/lock-corruption/page-fault requires non-zero garbage at
the `ipc_lock`/list offsets — a matter of what recycles the block in the
30 s window; INVARIANTS mtx owner checks make garbage at the lock word
likely to fire, but this was not observed within the time-boxed runs.
Honest classification per the contract: expected marker (panic) absent,
run exits, guest stays up ⇒ **not_reproduced**, reproduced=0,
impact=none. The lifetime defect itself is statically certain.

## Fix validation (behavioral)

Same patched fuse.ko as DF-3191/DF-3192 (drain in fuse_unmount +
post-enqueue dead recheck + remove-on-dead-entry):

- baseline cycle: umount returns ~1 s while the worker still sleeps
  (free happens under the in-flight IPC).
- patched cycle (fix_run.log): cycle wall time 83 s vs ~40 s — umount
  now blocks in the drain until the worker's ETIMEDOUT dequeues its fip;
  the worker's timeout path therefore runs on a **live** fmp
  (serial_full.txt in DF-3191's run for the patched boot shows
  `fuse_ipc_wait(uaf|926): timeout` executing after umount returned,
  i.e. on the still-referenced object), and the free happens only after
  both the drain and the unmount complete.
- no panic either way (the baseline crash is probabilistic), so the fix
  status is honestly **inconclusive** — the serialization the fix adds
  is demonstrated, the absence of a probabilistic crash is not provable
  in feasible time.

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