DF-3638 / verdict.json
{ "finding_id": "DF-3638", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Source-level confirmed (8/8 machine-checked assertions, run.log): when the DMA reload of a cloned retry/filtered frame fails, ath_tx_retry_clone() (sys/dev/netif/ath/ath/if_ath_tx.c:4567-4583) recycles nbf via ath_returnbuf_head() without dropping the node reference moved into it by ath_buf_clone() (if_ath.c:3186-3187, 3212-3213) and without clearing nbf->bf_m, which dangles at the mbuf chain freed inside ath_tx_dmasetup() (if_ath_tx.c:320/338/349). _ath_getbuf_locked() (if_ath.c:3080) does not sanitize bf_m/bf_node on reuse, so the recycled buffer carries the stale pointers. Net effect: one ieee80211_node/ath_node leaked per occurrence (slow station/memory exhaustion DoS on ath(4) hosts under memory pressure) plus a latent UAF pointer field one refactor away from a real use-after-free. Runtime reproduction impossible on the hardware-less guest (env.txt): honest not_reproduced; impact none reflects the run, not the ceiling.", "exploit_chain": "", "evidence": "findings/poc/DF-3638/run.log (8/8 PASS: clone ownership move at if_ath.c:3170+, error-path recycling without decref/mbuf-detach at if_ath_tx.c:4571-4585, _ath_getbuf_locked not clearing bf_m/bf_node); VERDICT.md; env.txt.", "kernel_refs": [ "sys/dev/netif/ath/ath/if_ath_tx.c:4540", "sys/dev/netif/ath/ath/if_ath_tx.c:4568", "sys/dev/netif/ath/ath/if_ath_tx.c:4579", "sys/dev/netif/ath/ath/if_ath_tx.c:294", "sys/dev/netif/ath/ath/if_ath_tx.c:4723", "sys/dev/netif/ath/ath/if_ath_tx.c:3495", "sys/dev/netif/ath/ath/if_ath.c:3170", "sys/dev/netif/ath/ath/if_ath.c:3080", "sys/dev/netif/ath/ath/if_ath.c:4844" ], "poc_changes": "No seeded PoC; authored verify_df3638.py (source-level artifact) - hardware-gated defect.", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 30, "guest_dirty": 0, "build_cmd": "python3 findings/poc/DF-3638/verify_df3638.py (static analysis of read-only sys/ tree)", "run_cmd": "sh findings/poc/DF-3638/run.sh", "code_hash": "71783994aef2e33da842d73d98c8c082484683c4e1d59cc7bc20642e7f062c3c", "notes": "Pass-2 (GLM 5.3) audit of if_ath_tx.c. Upstream-inherited (FreeBSD same path). Severity Low: node-ref leak + latent dangling pointer; trigger needs ath hardware + busdmamap NOWAIT failure during retry-clone of a BUSY aggregate/filtered frame.", "recommended_fix": "In ath_tx_retry_clone()s dmasetup-error path, ieee80211_node_decref(nbf->bf_node) and NULL bf_m/bf_node before ath_returnbuf_head(); harden _ath_getbuf_locked() to clear bf_m/bf_node on reuse.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff detaches node/mbuf on the error path and clears bf_m/bf_node in _ath_getbuf_locked (defense in depth matching the ath_freebuf KASSERT contract); cannot be runtime-validated without ath hardware.", "fix_evidence": "findings/poc/DF-3638/fix.diff (two hunks); run.log assertions 3, 5, 7 identify exactly the lines patched." } |