# DF-0917 — Use-after-free on fuse_ipc during tx completion

**Use-after-free on `fuse_ipc` (fip) in the FUSE IPC transaction-completion
path.** The `/dev/fuse` read/write completion handlers remove `fip` from the
reply/request TAILQ under `ipc_lock`, **drop the lock**, and then dereference
`fip` (`fip->reply`, `fip->request.buf`, `fip->done`) **with no reference
held**. If the tx originator (blocked in `fuse_ipc_wait`) times out (~35s),
`fuse_ipc_tx:270 fuse_ipc_put(fip)` drops the last ref and **frees fip**
while the device path is still mid-access → UAF (16-byte `fuse_buf` write +
pointer read + 4-byte atomic into a freed slab slot).

- **Severity:** High (memory corruption; root→kernel on default GENERIC).
- **Impact:** panic / corruption (DoS) when the race is won.
- **Reachability:** root-only on default GENERIC (`/dev/fuse` `root:operator`
  0660, `mount("fuse")` needs `uid==0`). Same root-only-FUSE threat model as
  DF-0915. NOT an unprivileged→root escalation.
- **Files:** `sys/vfs/fuse/fuse_device.c:118-223` (the bug),
  `sys/vfs/fuse/fuse_ipc.c:112-271` (the ref/free mechanics).

## Files in this evidence pack

| file | purpose |
|---|---|
| `harness.c` | deterministic pthread model of the UAF pattern (UNFIXED → `UAF CONFIRMED`; `-DFIXED` → `NO UAF`) |
| `fused0917.c` | live FUSE daemon that delays GETATTR replies ~35s to attempt the real race |
| `build.sh` / `run.sh` | exact build/run commands |
| `build.log` | full build output (both models + daemon) |
| `run.log` | 3x determinism runs of UNFIXED vs FIXED models |
| `fix_run.log` | live FUSE run on the **patched** fuse.ko (no regression) |
| `fix_build.log` | patched fuse.ko module build output |
| `fix.diff` | git-apply-able fix: `refcount_acquire` under lock + `fuse_ipc_put` after access (both read & write paths) |
| `env.txt` | guest uname / cc / module state |
| `VERDICT.md` | full narrative analysis |
| `manifest.json` | machine-readable catalog |

## Reproduce

```sh
# unprivileged deterministic proof (primary):
ssh dfbsd-maxx 'cd poc/DF-0917 && sh build.sh && ./harness && ./harness_fixed'
#   ./harness        -> "UAF CONFIRMED"
#   ./harness_fixed  -> "NO UAF (ref held across window)"

# live race attempt on the real kernel (root only, each iter ~35s):
ssh dfbsd 'kldload fuse && mkdir -p /mnt/fuse && cd /home/maxx/poc/DF-0917 && ./fused0917 2'

# build + install the fix (module-only), then re-test:
ssh dfbsd 'cd /usr/src && patch -p1 < /root/fix.diff && cd sys/vfs/fuse && make && cp /usr/obj/usr/src/sys/vfs/fuse/fuse.ko /boot/kernel/fuse.ko && kldload fuse'
```

## Expected results

- **UNFIXED `#0` kernel + `harness`:** `UAF CONFIRMED` (deterministic).
- **UNFIXED `#0` kernel + `fused0917`:** `stat()=ETIMEDOUT` (tx waiter freed
  fip), daemon `write()=ENOMSG` (fip already removed); guest stays up (the
  sub-µs race is not won in a bounded run — see VERDICT.md).
- **Patched `fuse.ko` + `harness_fixed`:** `NO UAF`.
- **Patched `fuse.ko` + `fused0917`:** identical to unpatched (no regression;
  FUSE mounts/stats/replies correctly; guest stays up).
