# DF-3451 — mld6_stop_listening mutates shared MLD link-local templates without mld6_token

Severity: Info (hardening / locking-consistency). No runtime PoC required per
contract (Info findings are skipped in Phase V); this pack documents the
source-level proof and the fix.

## Defect

`sys/netinet6/mld6.c:163-176` — `mld6_stop_listening()` writes the two
*shared global* link-local templates:

```c
mld6_all_nodes_linklocal.s6_addr16[1]   = htons(in6m->in6m_ifp->if_index);
mld6_all_routers_linklocal.s6_addr16[1] = htons(in6m->in6m_ifp->if_index);
```

and then (conditionally) calls `mld6_sendpkt(in6m, MLD_LISTENER_DONE,
&mld6_all_routers_linklocal)` — **all without holding `mld6_token`**.

Every other path that touches these globals holds the token:
- `mld6_start_listening()` mld6.c:146-160 (`lwkt_gettoken(&mld6_token)`),
- `mld6_input()` mld6.c:201-369 (token held across the whole handler, incl.
  the :269-270 / :327-328 template rewrites),
- `mld6_fasttimeo()` mld6.c:382-403.

## Effect

On an SMP system, `in6_delmulti()` (leaving a group, e.g. unprivileged
`IPV6_DROP_MEMBERSHIP` on interface A) races `mld6_input()`/`fasttimeo()` on
another CPU (interface B): the reader picks up a half-updated template —
specifically `s6_addr16[1]` embedding the *other* interface's if_index — and

1. the QUERY-path comparisons at mld6.c:292-293 test membership records
   against `ff02::1` with a foreign if_index embedded (mis-classification),
2. the DONE being sent is addressed to `ff02::2` with the wrong interface id
   (routers ignore it ⇒ lost leave ⇒ group traffic keeps flowing to the
   segment for the MLD listener-robustness interval).

No memory-safety violation (the globals are plain fixed-size structs), hence
Info; but it is a provable divergence from the file's own locking discipline
and the same family of shared-state races that produced DF-0691/DF-0692.

## Fix

`fix.diff` — wrap the whole body of `mld6_stop_listening()` in
`lwkt_gettoken(&mld6_token)` / `lwkt_reltoken(&mld6_token)`, mirroring
`mld6_start_listening()` (no new lock order: token-across-`mld6_sendpkt` is
exactly what start_listening already does).
