# DF-3192 — VERDICT

**status: reproduced / impact: panic (cross-driver type confusion + OOB write, root-gated)**

## Baseline run (stock INVARIANTS kernel #0, fuse.ko + cuse.ko loaded)

```
$ kldload fuse; kldload cuse; cc -O2 -Wall -o tc tc.c && ./tc
opened /dev/cuse fd=3 — cdevpriv is now a struct cuse_server*
mount(fuse, /mnt/tc3192, fd=<cuse>) — kernel now writes a fuse_mount layout into the cuse_server ...
```
serial console (run.log / panic.txt):
```
Cuse v0.1.37 @ /dev/cuse
fuse_init(kldload|831): FUSE ABI 7.28
panic: assertion "fmp->refcnt > 0" failed in fuse_mount at /usr/src/sys/vfs/fuse/fuse_vfsops.c:186
fuse_mount.part.6() at fuse_mount.part.6+0x777
sys_mount() at sys_mount+0x36b
```

The panic fires at fuse_vfsops.c:186 — i.e. **after** fuse_mount already
executed :176-185 against the cuse_server: `fmp->mp = mp`,
`fmp->dead = false`, `mtx_init()` ×3, `TAILQ_INIT()` ×2, `RB_INIT()`,
`fmp->devvp`, `fmp->cred = crhold(cred)` — a struct fuse_mount layout
stamped over the foreign `struct cuse_server` (cuse.c:768-777). The
KKASSERT reads the fuse-`refcnt` offset inside cuse_server memory
(M_ZERO'd at open) and reads 0 → INVARIANTS panic. This is direct proof
of the type confusion; with a victim object whose bytes at that offset
are non-zero, initialization continues (node tree inside the victim,
INIT IPC on the victim's TAILQs, `wakeup(fmp)` on a foreign address) and
the corrupted object detonates later inside its own driver (e.g.
`cuse_server_free` walking fuse pointers, `cv_destroy` on a clobbered
cv). fuse_mount layout is also larger than `struct cuse_server`, so the
writes at :176-199 are an OOB write into adjacent M_CUSE slab objects.

## Why it stops at root

Same privilege ceiling as DF-3191: the mount must pass
`caps_priv_check(cred, SYSCAP_NOMOUNT_FUSE)` (fuse_vfsops.c:153-155);
`SYSCAP_NOMOUNT_FUSE = 0xA4` carries no `__SYSCAP_NOROOTTEST`/`__SYSCAP_WHEELOK`
(sys/caps.h:96-97,227; sys/kern/kern_caps.c:328-331), so uid != 0 gets
EPERM before reaching the confusion. Not an unprivileged escalation;
root-gated cross-driver kernel memory corruption.

## Fix validation

`fix.diff` applied in guest /usr/src, fuse.ko rebuilt, rebooted:

```
$ ./tc
opened /dev/cuse fd=3 — cdevpriv is now a struct cuse_server*
mount(fuse, /mnt/tc3192, fd=<cuse>) ...
mount failed: Invalid argument        <- EINVAL at the fd==devvp check, BEFORE any foreign write
closing the cuse fd -> cuse_server_free dtor on corrupted object
survived close
TC_RC=0
```
No panic; guest up (fix_run.log). The EINVAL is returned by the new
`file->f_type != DTYPE_VNODE || file->f_data != devvp` check before
`devfs_get_cdevpriv()` is even called, so the cuse_server is never
touched (the PoC's canned "corrupted" text is informational only).

## Kernel references

- sys/vfs/fuse/fuse_vfsops.c:140-168 (name looked up, fd used, never cross-checked)
- sys/vfs/fuse/fuse_vfsops.c:176-199 (fuse_mount layout written into the foreign object)
- sys/vfs/devfs/devfs_core.c:2938-2958 (devfs_get_cdevpriv returns any driver's cdevpriv)
- sys/dev/misc/cuse/cuse.c:768-777 (cuse_server attached to /dev/cuse at open)
- sys/dev/virtual/nvmm/nvmm_dragonfly.c:315, net/netmap/netmap_freebsd.c:387, dev/misc/evdev/cdev.c:150, dev/misc/ipmi/ipmi.c:99 (other cdevpriv victims)

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