# DF-0691 PoC — mld6 MLD_LISTENER_QUERY NULL-deref/UAF race

## Status
Real locking race, **confirmed by source/locking analysis**, but **not live-reproducible
on this isolated guest** (see VERDICT.md). The MLD_LISTENER_QUERY handler excludes
loopback (`mld6.c:235`) and there is no IPv6 MLD querier on the QEMU slirp network, so
no inbound query reaches the vulnerable iteration; `ifnet_serialize_all` is netisr-only
(`netisr2.h:136`), so a kernel-thread harness cannot replay it.

## Harness
```
cd findings/poc/DF-0691
make            # -> mld6_race.ko
kldload ./mld6_race.ko
```
The harness replays `mld6_input`'s protospec read vs `in6_delmulti` on `vtnet0`. It
trips `ASSERT_NETISR_NCPUS` (the serializer is netisr-only) — which is itself the root
cause: `in6_delmulti` (a syscall thread) cannot take the serializer, so it NULLs+kfrees
`protospec` unsynchronized from the netisr-side `mld6_input` reader.

## Fix
Apply `fix.diff` to `/usr/src/sys/netinet6/{mld6.c,in6.c}` and rebuild the kernel
(INET6 is compiled into GENERIC). The fix:
1. `mld6.c`: NULL-check `protospec` after the read (`if (in6m == NULL) continue;`).
2. `in6.c:in6_delmulti`: defer `kfree(in6m)` to after `if_delmulti()` removes `ifma`
   from the list.

Validated: applies cleanly; the patched kernel compiles (`nativekernel` rc=0, both
`netinet6/in6.c` and `netinet6/mld6.c` compiled `-Werror` clean — see `fix_build.log`).
Runtime before/after not possible (bug not live-reproducible here).
