# DF-0599 — PoC: smb_vc_create NULL-iod panic via invalid charset

Privileged local **deterministic** kernel panic. `iconv_open` returns `ENOENT`
for any charset pair that has no registered converter; `smb_vc_create`'s error
path invokes `smb_vc_gone` → `smb_vc_disconnect` →
`smb_iod_request(vcp->vc_iod=NULL, ...)`, which executes
`SMB_IOD_EVLOCK(NULL)` = `smb_sl_lock(&NULL->iod_evlock)` and page-faults.

## Files

- `panic.c` — minimal reproducer (`SMBIOC_OPENSESSION` with an unregistered
  `ioc_localcs`).
- `build.sh` / `run.sh` — exact build/run commands.
- `fix.diff` — git-apply-able fix (NULL-guard in `smb_vc_gone` + clear
  `vc_iod` in `smb_iod_create` failure path).
- `VERDICT.md` — full root-cause confirmation + before/after fix validation.
- `manifest.json` — machine-readable artifact catalog.
- Logs: `build.log`, `run.log` (baseline panic), `panic.txt`, `fix_build.log`,
  `fix_run.log` (patched no-panic), `env.txt`.

## Preconditions

- DragonFly master DEV kernel `6.5-DEVELOPMENT #0` (unpatched baseline).
- The netsmb stack is a **loadable module**: `kldload smbfs` (pulls in
  `libmchain.ko` + `libiconv.ko`) creates `/dev/nsmb`.
- No iconv converter modules loaded (the default) ⇒ any charset name yields
  `ENOENT` from `iconv_open`. We use `"BOGUSCS-9"` (≤15 chars; `ioc_localcs`
  is 16 bytes).
- **Must run as root** — `/dev/nsmb` is mode `0700 root:wheel`
  (`smb_dev.c:356`). On this guest `mount_smbfs` is `0555 root:wheel` (not
  setuid), so the direct ioctl trigger is the repro path. The realistic
  unprivileged vector is a privileged confused-deputy mounting with a
  negotiated charset whose module is absent.

## Build & run

```
./build.sh                 # cc -Wall -O2 -o panic panic.c
sudo ./run.sh              # kldload smbfs ; ./panic   (run as root)
```

## Expected outcome

**Unpatched `#0` + original `smbfs.ko`** — immediate kernel panic (serial
console captured in `panic.txt` / `run.log`):

```
Fatal trap 12: page fault while in kernel mode
fault virtual address   = 0x58
Stopped at      smb_iod_request+0x58:   lock xaddl      %edx,0x58(%rbx)
db>
```

(`%rbx = 0` ⇒ `vc_iod == NULL`; `0x58` = offset of `iod_evlock` in
`struct smbiod`.)

**Patched `smbfs.ko` (rebuilt from `fix.diff`)** — the ioctl returns cleanly:

```
[*] DF-0599: issuing SMBIOC_OPENSESSION with localcs="BOGUSCS-9" (size=488)
[!] ioctl returned rc=-1 errno=2 (m)        # ENOENT, no panic, guest stays up
```

## Reproducing the fix validation

Because DF-0599 lives in the loadable `smbfs.ko` module (not the base kernel),
the fix is validated by rebuilding only that module:

```
# on the guest, with fix.diff applied to /usr/src:
cd /usr/src/sys/vfs/smbfs && make
cp /usr/obj/usr/src/sys/vfs/smbfs/smbfs.ko /boot/kernel/smbfs.ko && sync
kldload smbfs
./panic          # now returns ENOENT, no panic
```

See `VERDICT.md` for the full mechanism trace and before/after evidence.
