# DF-0974 — xpt_action_sasync_cb UAF race on async_callback

## Summary
`xpt_action(XPT_SASYNC_CB)` at `sys/bus/cam/cam_xpt.c:3324` enqueues a
taskqueue job carrying `task->data1 = cur_entry` (a pointer to an
`async_node`). If a concurrent `XPT_SASYNC_CB` call with the same
`(callback, callback_arg)` and `event_enable=0` arrives before the task
runs, it `SLIST_REMOVE + kfree(cur_entry)` while the task still references
it → UAF when the task runs (`cur_entry->callback(...)` at line 2906).

**Reachability:** `XPT_SASYNC_CB` is a CAM-internal CCB issued only by
`xpt_register_async()`, a kernel API. No in-tree caller pattern produces
the matching tuple needed to free a still-pending task's entry. No
unprivileged syscall can issue this CCB (`/dev/pass0`, `/dev/xpt0` are
root:operator mode 600; maxx cannot open them). Even kldload paths don't
naturally trigger the race. This is a defense-in-depth hardening gap.

## Files
- `fix.diff` — git-apply-able defense-in-depth fix:
  - deregister path no longer frees `cur_entry` (NULLs callback instead)
  - dispatch sites check `callback != NULL` before invoking
- `fix_build.log` — kernel build output showing `cam_xpt.c` compiles
  cleanly under `-Werror` with the fix applied
- `env.txt` — guest environment
- `VERDICT.md` — detailed analysis
- `manifest.json` — artifact catalog

## Reproduce
No runtime reproduction possible (kernel-internal race, not
unprivileged-reachable). To rebuild the patched kernel:
```sh
scp fix.diff dfbsd:/root/fix.diff
ssh dfbsd '/bin/sh -c "cd /usr/src && patch -p1 --forward < /root/fix.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC"'
```
