# DF-0884 — PoC: smbfs_readvdir UAF via dead-code vnode lock upgrade

## Verdict
**REPRODUCED (UAF via deterministic harness), then FIX VALIDATED.**

The bug is real: a dead-code vnode lock upgrade in `smbfs_readvnode` lets two
concurrent `read(2)` on a directory vnode run `smbfs_readvdir()` under a shared
lock, racing `smbfs_findnext()` against `smbfs_findclose()` and using-after-free
the `smbfs_fctx`. The authored `fix.diff` closes it: the patched `smbfs.ko`
restores `vn_islocked()` + the conditional `vn_lock(LK_UPGRADE/DOWNGRADE)` (the
dead code is gone at the object-code level), and the fixed-mode harness shows
the race is eliminated.

## Reachability (harness, not live mount)
The live trigger requires a mounted SMB share (an SMB server reachable from the
kernel client). This isolated KVM guest has no network beyond QEMU user-mode NAT
to the host and no SMB server, so the bug is proved by a **deterministic
harness** that transcribes the exact code path (per the DF-0598 / DF-0599 smbfs
precedent). The harness models the SMB network round-trip inside
`smbfs_findnext()` as a controllable interleaving point and uses a poisoned
allocator to detect the write-UAF on the freed `smbfs_fctx`.

## Build
```
./build.sh          # cc -O2 -pthread -o harness harness.c
```

## Run
```
./run.sh buggy      # baseline: expect  >>> UAF CONFIRMED
./run.sh fixed      # fix model: expect  >>> FIXED: no UAF
```

## Expected (bug present)
```
[*] After Thread B ran: A's ctx=0x... freed? YES (poison=0xDD)
[+] A wrote ctx->f_attr.fa_ino (0xcafebabe) into an object that was ALREADY freed by Thread B's smbfs_findclose()
>>> UAF CONFIRMED: smbfs_findnext wrote through freed smbfs_fctx (dead-code lock upgrade lets two read(2) on a VDIR run smbfs_readvdir concurrently)
```
Deterministic: confirmed across 3 consecutive runs (`run.log`, `run.2.log`,
`run.3.log`).

## Fix validation (Phase 8)
`fix.diff` was applied to `/usr/src/sys/vfs/smbfs/smbfs_io.c`, `smbfs.ko` was
rebuilt (`fix_build.log`, `-Werror` clean, rc=0), and the patched module was
installed + `kldload`-ed cleanly. Object-code proof (`disasm_evidence.txt`):

| call in `smbfs_readvnode` VDIR branch | unpatched | patched |
|---|---|---|
| `vn_islocked` | **0** | **1** |
| `vn_lock`     | **0** | **2** (upgrade + downgrade) |

The fixed-mode harness (`fix_run.log`) shows the upgrade serializes the two
readers: `>>> FIXED: no UAF`.

See `VERDICT.md` for the full mechanism walkthrough and `manifest.json` for the
artifact catalog.
