From 0b78eb6e033fd9516501b06a920a3a4f8b31fdcf Mon Sep 17 00:00:00 2001 From: ruv Date: Thu, 11 Jun 2026 21:00:32 -0400 Subject: [PATCH] =?UTF-8?q?fix(hardware):=20drop-instead-of-truncate=20sub?= =?UTF-8?q?carrier=20count=20in=20802.11bf=20bridge=20(ADR-157=20=C2=A7B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpportunisticCsiBridge::ingest built CsiReportPayload.n_subcarriers via `self.amp_accum.len() as u16`, which would silently wrap a count above 65_535. Replace with `u16::try_from(...).ok()?` (drop-instead-of-truncate). Disclosed honestly as defense-in-depth on an UNREACHABLE path: ingest already gates subcarrier_count > MAX_REPORT_SUBCARRIERS (484) at entry and report.validate() rejects oversized counts downstream, so the cast can never wrap in practice. Correct-by-construction rather than gate-dependent; no behavior change, no new test (the gate prevents the input that would exercise it). Co-Authored-By: claude-flow --- .../src/ieee80211bf/transport.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/v2/crates/wifi-densepose-hardware/src/ieee80211bf/transport.rs b/v2/crates/wifi-densepose-hardware/src/ieee80211bf/transport.rs index 07c014b4..81f56ed6 100644 --- a/v2/crates/wifi-densepose-hardware/src/ieee80211bf/transport.rs +++ b/v2/crates/wifi-densepose-hardware/src/ieee80211bf/transport.rs @@ -289,8 +289,16 @@ impl OpportunisticCsiBridge { } let scale = self.frames_in_batch as f64; + // Drop-instead-of-truncate: `as u16` would silently wrap a subcarrier + // count above 65_535. That count is already gated to + // `<= MAX_REPORT_SUBCARRIERS` (484) at `ingest`'s entry, so this branch + // is unreachable in practice — but `try_from().ok()?` makes the + // construction correct-by-construction rather than relying on the + // upstream gate, and drops the batch cleanly if the invariant ever + // changes (ADR-157 §B1, defense-in-depth — not a live bug). + let n_subcarriers = u16::try_from(self.amp_accum.len()).ok()?; let payload = CsiReportPayload { - n_subcarriers: self.amp_accum.len() as u16, + n_subcarriers, amplitudes: self.amp_accum.iter().map(|a| (a / scale) as f32).collect(), phases: self .phase_sin_accum