mld6_stop_listening mutates the shared MLD link-local templates without mld6_token β torn if_index embedding mis-addresses MLD DONE and mis-classifies the all-nodes comparison
Summary
mld6_stop_listening writes the shared globals mld6_all_nodes_linklocal/mld6_all_routers_linklocal (s6_addr16[1] = htons(if_index)) and calls mld6_sendpkt without taking mld6_token, while every other path touching those globals holds it (start_listening, mld6_input including the rewrites, fasttimeo). On SMP, an unprivileged group-leave on interface A races query/timeout processing on interface B: the reader picks up a torn embedding, the QUERY walk's ff02::1 comparison mis-classifies, and the DONE is addressed ff02::2 with a foreign interface id so routers drop it - lost leave notification, group traffic persists for the robustness interval. No memory-safety impact (fixed-size globals) - hardening/consistency. Source-level proof only (Info; Phase V skipped per contract); fix diff authored+validated in pack. Fix: wrap the body in mld6_token, mirroring start_listening.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3451 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 1.1 KB | β raw | |
| fix.diff | β | 823 B | view raw | |
| verdict.json | β | 2.0 KB | view raw | |
| manifest.json | β | 608 B | view raw |
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:
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
- the QUERY-path comparisons at mld6.c:292-293 test membership records
against
ff02::1with a foreign if_index embedded (mis-classification), - the DONE being sent is addressed to
ff02::2with 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).
DF-3451 VERDICT
Status: untested (Info-severity finding - Phase V intentionally skipped per contract). Source-level proof complete; fix authored and diff-validated.
The defect, effect analysis, and the fix are in README.md. Summary: mld6_stop_listening (mld6.c:163-176) is the only MLD path mutating the shared mld6_all_{nodes,routers}_linklocal templates and driving mld6_sendpkt without mld6_token. Impact ceiling: lost/mis-addressed MLD DONE notifications and mis-classified all-nodes comparisons under SMP concurrency (interface leave on one CPU racing query/timeout processing on another). No memory unsafety. fix.diff adds the token, mirroring start_listening (mld6.c:146).
Not re-reported (same root cause as DF-0691, covered by its fix): the MLD_LISTENER_REPORT path's post-lookup in6m write at mld6.c:354-357.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
['README.md (source citations mld6.c:146-160 vs :163-176 vs :201/:269/:327/:382)', 'fix.diff']
PoC changes
none - no runnable PoC produced for an Info finding per contract
Verified recommended fix
Wrap mld6_stop_listening's body in lwkt_gettoken(&mld6_token)/lwkt_reltoken(&mld6_token), mirroring mld6_start_listening.
Verdict
Info-severity locking-consistency defect, source-proven, deliberately not exercised at runtime per contract (Info findings skip Phase V): mld6_stop_listening (sys/netinet6/mld6.c:163-176) writes the shared globals mld6_all_nodes_linklocal / mld6_all_routers_linklocal and sends the MLD DONE without mld6_token, while every other path touching those globals (start_listening mld6.c:146, mld6_input mld6.c:201, fasttimeo mld6.c:382) holds it. Concurrent leave vs query/timeout on an SMP box yields torn if_index embeddings: mis-classified ff02::1 comparisons in the QUERY walk and DONE packets addressed with a foreign interface id (lost leave notification, group traffic persists for the robustness interval). No memory-safety impact - fixed-size globals - hence Info. fix.diff mirrors start_listening's token-across-send pattern; no new lock order introduced.
No comments yet.