# DF-5415 — `fw_kqfilter` dereferences `d->ir` without a NULL check → kernel panic (and EVFILT_WRITE hooked to the receive queue)

**File:** `sys/bus/firewire/fwdev.c` · **Severity:** Medium · **Bucket:** base:hw (local DoS) · **Confidence:** certain
**Status: reproduced (kernel panic) on the QEMU guest.**

## Root cause (production path:line)

`fw_kqfilter()` reads `ir = ((struct fw_drv1 *)dev->si_drv1)->ir`
(`fwdev.c:721`) and, for **both** `EVFILT_READ` and `EVFILT_WRITE`
(`fwdev.c:725-737` — the write filter should use `d->it`, it uses `ir`),
executes `klist = &ir->rkq.ki_note; knote_insert(klist, kn)`
(`fwdev.c:739-740`).  `ir` is NULL until the first `FW_SRSTREAM` succeeds
(`fwdev.c:494`), so `knote_insert` operates at `NULL +
offsetof(struct fw_xferq, rkq)` = **NULL+0x98**.

Any holder of an open `/dev/fwN.M` descriptor (device mode 0660, group
operator) can reach this before configuring a stream:

```c
fd = open("/dev/fw0.0", O_RDWR);
kq = kqueue();
kevent(kq, &(struct kevent){ .filter=EVFILT_READ, .flags=EV_ADD }, 1, ...); /* panic */
```

## Demonstration (guest, test 5, production fw_kqfilter)

```
df5415: production fw_kqfilter with d->ir=NULL, offsetof(fw_xferq, rkq)=152
panic: assertion "kn->kn_status & KN_PROCESSING" failed in knote_insert at kern_event.c:1890
knote_insert() at knote_insert+0x5a
fw_kqfilter() at fw_kqfilter+0x8e
df5413_fire_sysctl() ...
```

The NULL-adjacent memory access inside `knote_insert` (reading the knote
status through the NULL-derived klist) panics the kernel with `fw_kqfilter`
pinned in the trace (`panic.txt`).  Guest down.

## What to run

```
./build.sh
sh run.sh      # kern.df5413_test=5  -> guest panics; capture serial console
```

## Fix

`fix.diff` (shared) returns `ENODEV` when `ir == NULL` before touching
`rkq`.  Validated: `fw_kqfilter returned 19` (ENODEV), guest alive
(`fix_run.log`).  Upstream should also hook `EVFILT_WRITE` to `d->it`
(as modern FreeBSD does).
