)
RuView CSI 反序列化边界安全审查wifi-densepose-core与wifi-densepose-cli加固验证ADR-172【免费下载链接】RuViewπ RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single pixel of video.项目地址: https://gitcode.com/GitHub_Trending/wi/RuView本文基于 ADR-172: wifi-densepose-cli wifi-densepose-core CSI-Deserialiser Security Review 展开。RuViewπ RuView将普通 WiFi 信号转化为实时的空间智能、生命体征监测与存在感知而这一切的数据地基是对 CSI信道状态信息数据包的解析——即本文所讲的反序列化边界。这篇技术指南还原一次真实的安全审查全过程它验证了两种典型的不可信输入UDP CSI 数据包、二进制 canonical 帧在 RuViewv2/Rust 工作区中是如何被无 panic、无越界分配地处理的并介绍了用回归测试钉住既有防护regression pin的做法。读完你将掌握为什么wifi-densepose-core是 12 个下游 crate 的力放大点、如何用代码证据判定NaN 状态污染缺陷类是否根植于共享原语以及 4 个反序列化安全防护在源码与测试中的具体落点。1 背景一次超出 SOTA安全清扫中的盲区补查RuViewv2/工作区以 workspace 形式组织了大量 Rust crate成员清单见 v2/Cargo.toml。在 beyond-SOTA 安全清扫分支feat/v2-beyond-sota-sweep中审查人员对每个 crate 逐一检查是否存在真实、可复现的缺陷。清扫结束时发现有两个 crate 此前没有专门的独立安全 ADRwifi-densepose-core—— 依赖根所有 12 个下游 cratewifi-densepose-calibration、-vitals、-geo、-cli等都直接或间接依赖它提供类型、trait、错误类型与 CSI 帧原语。这里的缺陷是力放大器任何共享原语中的 bug 都会被每个消费者继承。wifi-densepose-cli—— 面向用户的入口点calibrate/calibrate-serve/enroll/train-room/room-watch等命令另有 MAT-gated 的mat export它会解析来自网络的不可信 UDP CSI 数据包也会处理操作员提供的路径。换言之这两个 crate 恰好构成 RuView 感知管线中最重要的不可信输入边界crate角色暴露面wifi-densepose-core库根12 个下游 crate 依赖canonical 二进制帧反序列化回放/转发边界wifi-densepose-cli网络对外的用户入口UDP CSI 包解析面向局域网任意发送方、HTTP APIcalibrate-serveADR-172 的目的不是继续打补丁而是为已存在但未测试的防护补上回归测试防止未来某次重构悄悄移除它们时 CI 没有任何反应。2 承重问题NaN 状态污染缺陷类是否根植于 core推动这次 core 审查的是一个非常具体的技术假设。在此前的三次审查中审查人员在依赖 core 的 cratewifi-densepose-calibration、wifi-densepose-vitals、wifi-densepose-geo中发现了一类系统性 bugNaN 状态污染NaN-state-poisoning一个非有限NaN/Inf的输入被闩锁进持久化的滤波器/累加器状态IIR 的y1/y2、running mean、Welford/von-Mises 累加器、体素网格导致静默的、永久性的特征失效。承重问题于是变成这个 bug 类是起源于wifi-densepose-core中的某个共享原语那么正确修复应是一处根因修复还是每个下游 crate各自独立实现了累加器那么此前三处局部修复已经完备、不需要动 core2.1 结论NO—— NaN 类不在 core 中审查证据表明wifi-densepose-core不暴露任何有状态累加器没有 Welford / running-mean没有 von-Mises / 环形均值没有 IIR/biquad 滤波器状态没有体素网格。实测证据MEASURED对core/src进行grep模式为welford|von_mises|biquad|y1|y2|running_mean|accumulat|voxel|self.*命中的只有三类内容InvalidState错误枚举变体与 reset state 有关的文档注释一处仅测试使用的 LCG线性同余随机数生成器。零有状态逻辑。core 中仅有的浮点数学是构造期的投影CsiFrame::new通过mapv计算 amplitude/phase与纯无状态的utils函数没有任何跨帧持久化的东西。旁证Corroborationwifi-densepose-calibration::Features::from_series源码位于 extract.rs在实现上已经先过滤非有限样本再计算特征pub fn from_series(series: [f32], fs: f32) - Features { let clean: Vecf32 series.iter().copied().filter(|v| v.is_finite()).collect(); if clean.is_empty() { return Features::ZERO; // 无有限样本 → 归零而不是传播 NaN } // ... 基于 clean 计算 mean/variance/motion/embedding ... }测试同一文件 extract.rs也明确验证了这一点混入f32::NAN、f32::INFINITY的序列仍能产出有限的特征值全 NaN/Inf 序列精确等于Features::ZERO。这一旁证恰好说明下游修复是各自独立实现的从而确认了每个 crate 自己滚动实现累加器、每个局部修复正确且完备。由此得到两个推论在 core 里做修复会是空操作那里没有需要修复的东西NaN 状态污染这一类缺陷是下游局部模式downstream-local pattern而非 core 根因缺陷共享原语中不存在隐藏的第 4 个实例。3 发现汇总4 条回归钉guards 已存在现在被测试覆盖审查的最终决策是干净且有证据clean-with-evidence产出了 4 条回归测试钉regression pin。这 4 条全部属于防护本来就存在、只是没人测试的情形没有改动任何生产代码#位置防护既有回归钉测试实测证据1coretypes.rsfrom_canonical_bytes在Vec::with_capacity(rows*cols)之前做saturating_mul的形状-长度检查canonical_decode_oversized_shape_is_bounded_not_allocated移除防护后在 types.rs:800 处capacity overflowpanic带防护则通过2coretypes.rs解码器类型化CanonicalDecodeError绝不 paniccanonical_decode_never_panics_on_arbitrary_bytesfuzz 扫描任意字节上无 panic3clicalibrate.rsUDP 解析器在Array2::zeros(n_antennas*n_subcarriers)之前的长度检查test_parse_csi_packet_oversized_claim_is_rejected_not_allocated2 KB 数据包声称 255×65535 → 返回None无分配4clicalibrate.rsUDP 解析器畸形输入一律返回Nonetest_parse_csi_packet_never_panics_on_arbitrary_bytesfuzz 扫描任意 UDP 字节上无 panic下面两节分别深入到这两条边界的源码实现。4wifi-densepose-corecanonical 帧解码器为何失败即关闭4.1 类型化错误CanonicalDecodeErrorcore 的 canonical 解码器位于 types.rs。它不是一个返回空的宽松解码器而是对每种畸形类别都返回类型化错误的严格解码器pub enum CanonicalDecodeError { Truncated { at: usize, need: usize }, // 缓冲提前结束 BadDiscriminant { field: static str, value: u8 }, // 未知判别字节 BadDeviceId, // device id 非 UTF-8 PayloadMismatch { rows, cols, expect, found },// 声明形状与实际负载不符 TrailingBytes(usize), // 负载后有多余字节 ReservedNotZero { field: static str }, // 保留区必须为全零 }其中ReservedNotZero的语义值得展开如果允许保留区非零就会让两个不同的字节串解码成同一帧——一旦经过解码→重编码的回放往返被伪造的字节将与真实 canonical 编码不可区分。严格性保证了在可接受域上的单射性injectivity任何被接受的字节串重编码后都精确等于它自己。这正是失败即关闭fail closed的设计。4.2 越界分配防护saturating_mul先于with_capacity解码入口是CsiFrame::from_canonical_bytestypes.rs:745。它先用一个Cursor顺序读取帧头UUID、时间戳、device id、频段判别、天线配置、保留区等最后读取负载形状并分配let rows c.u32()? as usize; let cols c.u32()? as usize; // 关键饱和乘法 —— 先算该形状需要多少字节再分配 let expect rows.saturating_mul(cols).saturating_mul(16); let found bytes.len() - c.at; if found expect { return Err(CanonicalDecodeError::PayloadMismatch { rows, cols, expect, found }); } let mut samples Vec::with_capacity(rows * cols);这段代码回答了一个经典的无界内存 DoS问题如果攻击者伪造帧头、把rows × cols声明成天文数字例如u32::MAX × u32::MAX而分配动作Vec::with_capacity(rows * cols)发生在长度校验之后攻击者就能用几个字节的头部驱动一次多 GB 的分配。这里在分配前用两个saturating_mul计算该形状按 16 字节一个复数样本所需的字节数并与输入剩余长度比对found expect则直接返回类型化错误。因为saturating_mul不会溢出expect恒大于等于实际需要的容量下限校验一旦通过容量就被调用者已经持有的输入长度所约束不会 OOM。回归钉源码内联于同一文件见 types.rs:1651-L1671把这一行为固化下来测试构造一段合法 canonical 帧把末尾的(rows, cols)覆盖成u32::MAX × u32::MAX并截断真实负载断言解码结果必须是Err(CanonicalDecodeError::PayloadMismatch{..})而不是 panic 或分配。4.3 无 panic 保证全前缀扫描 LCG fuzz第二条回归钉 types.rs:1677-L1703 把panic-on-adversarial-input 0固化为测试全前缀扫描对合法编码的每一个前缀good[..n]n从 0 到全长都调用一次解码断言绝不 panic——任何截断点都必须落到类型化错误上确定性 LCG fuzz用固定种子的线性同余发生器生成 0~399 字节的任意字节流逐一解码断言全程无 panic。说明core 测试中使用 LCG 是为了确定性这正与上一节core 无有状态业务逻辑的结论呼应——该 LCG 仅是测试工具不构成任何跨帧状态。5wifi-densepose-cliUDP CSI 包解析器的不可信输入边界5.1 线格式与长度校验CLI 的 UDP 入口是parse_csi_packetcalibrate.rs。它解析一个 20 字节头 IQ 采样对线格式文件头部注释为偏移字节数字段04magic0xC511_0001LE41node_id51n_antennasu862n_subcarriersLE u16——ESP32-C6 HE-SU 帧为 256按单字节读取会把256 0x0100 LE误读成 0 个子载波84中心频率MHz124sequence162RSSI / noise floori8182ppdu type / tier202×n_antennas×n_subcarriersIQ 对i_val (i8)、q_val (i8)解析流程的关键防护点在 calibrate.rs:276-L283let n_pairs n_antennas * n_subcarriers; let iq_start 20usize; if buf.len() iq_start n_pairs * 2 { return None; // ① 分配前先做长度校验 } // ② 只有校验通过后才申请 [n_antennas, n_subcarriers] 的 ndarray let mut data Array2::Complex64::zeros((n_antennas.max(1), n_subcarriers.max(1)));因为默认 UDP 监听地址是0.0.0.0calibrate.rs:56-L57 附近局域网任意主机都能向该端口发送任意字节。若没有 ① 处的长度检查一个 2 KB 的普通数据包只要把头部的n_antennas255、n_subcarriers65535填满就能触发Array2::zeros申请 255×65535 的复数矩阵——一次内存 DoS。现在该校验在分配之前返回None。除了恶意分配防御还覆盖长度小于 20 字节直接Nonemagic 不匹配直接None频率转 u16 失败unwrap_or(0)兜底。整套解析器遵循**畸形即None**的约定None模式贯穿调用方 calibrate.rs:152 的处理逻辑。两条回归钉源码内联测试test_parse_csi_packet_oversized_claim_is_rejected_not_allocatedcalibrate.rs:477-L492构造 255×65535 声称的极小包断言返回Nonetest_parse_csi_packet_never_panics_on_arbitrary_bytescalibrate.rs:500-L519多 tier 下用任意字节 fuzz 扫描断言无 panic且合法 magic 撒谎的子载波数 无负载也必须优雅返回None。5.2 NaN 处理与空帧安全core 在数值边界上同样干净Confidence::new拒绝 NaN构造置信度时先检查if !(0.0..1.0).contains(value)再收下数值types.rs:265 附近NaN 落在区间之外 ⇒Err。配套测试断言0.0/1.0/0.5合法、-0.1/1.1被拒types.rs:1437-L1441compute_bounding_box/to_flat_array对 NaN 容忍f32 的 min/max 语义天然忽略 NaN空帧安全amplitude_variance/mean_amplitude在空Array2上无 panicndarray 0.17 返回有限值或None。6calibrate-serveHTTP 面的路径穿越与鉴权UDP 之外CLI 还通过calibrate-serve暴露 HTTP API。审查确认了三条既有防护均有测试路径穿越防护sanitize_room_idcalibrate_api.rs:107-L118所有客户端提供的room_id/bank/baseline在进入文件名/写路径前都会被清洗——只保留 ASCII 字母数字与_、-截断到 64 字符空结果回退到default。这直接杜绝了../与绝对路径穿越Bearer 鉴权 非回环绑定警告token 通过--token#[arg(long, env CALIBRATE_TOKEN)]见 calibrate_api.rs:100-L101从CALIBRATE_TOKEN环境变量读取从不嵌入代码HTTP API 默认只绑定127.0.0.1一旦绑定非回环地址而 token 缺失服务端会打印警告calibrate_api.rs:349-L353 附近mat export例外它写的是操作员提供的PathBuf——这是 CLI 的合理行为不属于需要防御的不可信面。7 验证结果审查以测试为最终裁判以下数据为 ADR-172 记录的实测结果cargo test -p wifi-densepose-core # 35 → 37 lib 通过0 失败3 doctests cargo test -p wifi-densepose-cli --no-default-features # 24 → 26 通过0 失败 cargo test --workspace --no-default-features # exit 00 失败 python archive/v1/data/proof/verify.py # VERDICT: PASS其中新增的2core 37 vs 35、cli 26 vs 24正是本次加入的 4 条回归钉。proof 脚本哈希f8e76f21a0f9852b70b6d9dd5318239f6b20cbcb4cdd995863263cecdc446f7a保持不变——这说明 core/cli 都不在信号 proof 路径上审查未对既有推理管线产生任何改变。8 影响与结论正面影响两个 CSI 反序列化器库根与网络对外的 CLI即不可信输入的两处边界的 DoS 防护现在被钉死在测试里——未来任何一次去掉长度检查的重构都会让 CI 失败NaN 状态污染缺陷类被定性为下游局部模式审查者不再需要怀疑存在共享根因此前三个 crate 的局部修复被确认完备。负面影响无——纯测试变更零行为/API 变化。中性影响core 部分的记录同时体现在 ADR-127 §9共享安全审查日志本文档对应的 ADR-172 是wifi-densepose-cli审查的 canonical 记录。可交叉查阅ADR-127共享审查日志 §9、ADR-136既有的 CSI 反序列化 DoS 验收标准、ADR-151calibrate/calibrate-serve按房间校准能力面。9 可借鉴的审查方法论把 ADR-172 还原成方法论它提供了一套可复用的共享原语安全归因流程先归因再修复遇到在多个下游 crate 中复现的系统性缺陷先问它是否起源于共享依赖用一次性 grep有状态原语关键词集合验证共享层是否存在有状态逻辑避免在错误层级做修复区分缺防护与缺测试本审查 4 个发现全部属于后者——防护早已由 ADR-136 验收标准等落地真正缺失的是能防止未来重构删除防护的回归钉。为已存在但未测试的守卫补测试是成本极低、收益明确的安全投入优先选择确定性验证fuzz 用固定种子的 LCG 而非随机保证任意字节 panic 扫描可在任何机器上复现把 DoS 校验放在分配之前Vec::with_capacity/Array2::zeros之前必须有基于输入长度、用saturating_mul计算的形状校验使声称的大小永远无法驱动超出输入长度的分配。【免费下载链接】RuViewπ RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single pixel of video.项目地址: https://gitcode.com/GitHub_Trending/wi/RuView创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考