尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

RuView OccWorld Candle 检查点加载加固实录:修复 ML 权重加载中的 dtype 位宽放大崩溃与退化输入 panic

发布时间:2026/9/9 23:29:30

资讯中心
01
ARTICLE

RuView OccWorld Candle 检查点加载加固实录:修复 ML 权重加载中的 dtype 位宽放大崩溃与退化输入 panic

RuView OccWorld Candle 检查点加载加固实录:修复 ML 权重加载中的 dtype 位宽放大崩溃与退化输入 panic
RuView OccWorld Candle 检查点加载加固实录修复 ML 权重加载中的 dtype 位宽放大崩溃与退化输入 panic【免费下载链接】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-179CodenameOCCWORLD-DTYPE 为骨架结合仓库内wifi-densepose-occworld-candlecrate 的源码与测试完整还原一次针对 Candle 推理引擎的“检查点加载加固”安全整改一个标着#[forbid(unsafe_code)]的 Rust crate 仍可能通过 malformed 权重触发 panic在 WASM 场景下即 abort。读者将从中掌握SafeTensors 权重加载时 dtype 映射字节级一致性为何致命、如何用“fails-on-old”回归测试钉死三类 bug以及 fail-closed 边界防护在 ML 系统边界上的落地写法。该整改同时宣告了 RuView“Milestone #9未门控 crate 安全清扫”的收官。一、背景Candle 推理 crate 的真实风险面wifi-densepose-occworld-candle是 RuView 将 OccWorld一种 occupancy-world 模型核心结构为VQ-VAE 覆盖 occupancy token 的 Transformer以 Candle 框架移植为原生 Rust 推理实现的结果源头见 ADR-147 描述的 world-model 路线crate 位于 v2/crates/wifi-densepose-occworld-candle。其包描述直言其定位ADR-147 — OccWorld TransVQVAE inference ported to Candle (Rust-native, no Python IPC)也就是说这份 Rust 实现要在没有 Python IPC 的情况下仅凭一个.safetensors检查点文件在 Candle 上完成整条真实前向把过去的 occupancy 体素网格编码为离散 token由 Transformer 预测未来 token再解码回语义体素分类。1.1 为什么“无 unsafe 代码”不等于“无崩溃风险”crate 的 Cargo.toml 明确声明了这条纪律[lints.rust] unsafe_code forbid missing_docs warn但正如 ADR 开门见山指出的ML crate 的真实风险面是退化输入degenerate-input与畸形权重malformed-weights处理。unsafe_code forbid只能阻止内存不安全的直接访问却无法阻止一个 tensor 运算在遇到不一致的 shape时触发 panic——而 panic 在 Rust 中本身就是一种可被外部输入引发的拒绝服务DoS在 WASM 运行时下更是直接 abort。对这份 crate 而言外部可控的输入面只有两个检查点文件SafeTensors 权重——唯一的文件输入面predict收到的 past_occupancy tensor——由调用方外部提供的推理输入。ADR-179 的结论是crate 内共有三个可达reachablebug需要修复每一项都用一个“在旧代码上会失败fails-on-old、在新代码上通过”的测试钉死pinned其余风险维度则用证据逐一证清attest clean。1.2 模型配置快照理解 shape 校验的前提要理解后文的边界守卫需要先知道默认配置下的几何与容量约束。见 src/config.rs 的OccWorldConfig::default()配置项默认值含义对照 Python 参考实现grid_h / grid_w / grid_d200 / 200 / 16体素网格 X/Y/Zocc_size推理输入 shape 为(1, 16, 200, 200, 16)num_classes/free_class18/17语义类别数 / “空闲/未知”类索引nuScenes 约定base_channels/z_channels64/128ResNet 编码器基础通道 / 潜在通道codebook_size/embed_dim512/512VQ 码本大小与每项维度num_frames15输入上下文帧数时间位置嵌入表容量为num_frames*2行token_h/token_w50/50VQVAE 编码后的 token 网格H/4、W/4num_heads/num_layers/ffn_hidden8/2/2048Transformer 结构参数config.rs注释同步提醒任何一项改动都必须与权重检查点中的 tensor shape 严格一致因为 shape 已被烘焙进 SafeTensors 文件。二、三项修复概览ADR 决策表ADR-179 将三项发现整理成如下决策表全部在 Windows 上实测MEASURED#严重度位置问题修复1HIGHmodel.rsdtype 映射任何含 int32 tensor 的检查点都会崩溃DoS on loadI32 字节缓冲4 B/元素被交给from_raw_buffer(.., I64, shape, ..)Candle 按 8 B/元素推导elem_count元素数被减半而 shape 保持不变 → tensor 声称的存储量是实际的 2 倍读取时在 candle-core 内 slice-OOB panic将I32 → DType::I32、I16 → DType::I16均为 Candle 一等 dtype2LOWinference.rs::predictframe/batch 维未校验此前只校验 H/W/Df_in num_frames*2会越界索引时间嵌入表现为晦涩的 candleInvalidIndex错误零 frame/batch 会把零元素 tensor 送进 reshape/conv 流水线边界守卫以清晰的ShapeMismatch拒绝零/超容 framebatch5 个 pin3LOWvqvae.rsz.elem_count() / last公开方法VQCodebook::encode在 rank-0 / 空末维 tensorlast 0上除零 panicfail-closed 守卫返回明确错误HIGH 项是全文最值得注意的一条crate 自身的 dtype 映射“击败”了上游safetensors::validate()的字节长度保证——malformed / 被拓宽的权重能抵达会 panic 的 candle 运算恰恰就发生在这一处。三、Finding #1HIGHint32 dtype 位宽放大导致的加载即崩溃3.1 根因机制安全审查发现model.rs中 safetensors dtype → candle dtype 的转换函数原先存在一行看似无害实则致命的映射// 旧代码bug——ADR 记录于 model.rs:95 Dtype::I32 Some(DType::I64), // ❌ 把 4 B/元素的 int32 声明成 8 B/元素的 int64问题出在 Candle 的Tensor::from_raw_buffer(data, dtype, shape, device)的推导逻辑它根据data.len() / dtype.size_in_bytes()计算元素个数。当一个 int32 字节缓冲每个元素 4 字节被交给DType::I64路径时elem_count data.len() / 8元素数立即被减半而调用方传入的 shape 依然是原始 shape。其结果是构造出一个声明 shape 所声称的元素数恰好是实际存储的 2 倍的 tensor——一个静默的 shape/storage 不一致。model.rs当前代码的注释给出了精确到 panic 报文的现场还原handing an int32 byte buffer (4 bytes/elem) to the I64 path (8 bytes/elem) halves the element count while keeping the original shape, producing a tensor whose declared shape claims twice as many elements as its storage holds. That silent shape/storage mismatch panics (slice OOB) the moment the tensor is read.具体到 ADR 记录的崩溃报错range end index 6 out of range for slice of length 3——6 元素 shape 的数据实际只有 3 个元素的存储。3.2 为什么这是真实攻击面而非理论问题PyTorch 导出中 int32/int64 tensor 非常常见——index、buffer如 VQ 码本的embedding索引、量化相关的整数张量在导出时普遍保留整数 dtype。因此“任何含 int32 tensor 的检查点”并不是恶意构造的稀有样本而是一个常规合法文件即可能触发的 DoS攻击者只要诱导加载一个含整数张量的 checkpoint模型加载即崩溃。这也正是该问题被评为HIGH的原因。更深刻的一点是safetensors::validate()上游本有溢出检查的nelements * dtype.size()与声明字节区间的比对、contiguous-offset 与 buffer-length 校验理论上能拒绝 malformed 文件。但这条防线校验的是文件头声明的 dtype 与文件字节长度的一致性——而这里 bytes 是真的 int32 数据、文件自洽合法背叛保证的是 crate 自己错误声明出来的 candle dtype。ADR 的原话是Finding #1 是“the one place the crate defeated that guarantee”。3.3 修复后的 dtype 映射当前源码修复后的safetensor_dtype_to_candle位于 src/model.rs原则是“每个 safetensors dtype 映射到字节宽度一致的一等 candle dtype不擅自拓宽”match dt { Dtype::F32 Some(DType::F32), Dtype::F64 Some(DType::F64), Dtype::F16 Some(DType::F16), Dtype::BF16 Some(DType::BF16), // I32 MUST map to DType::I32, not I64... Dtype::I32 Some(DType::I32), Dtype::I64 Some(DType::I64), // I16 is also a first-class Candle dtype (2 bytes/elem); map it // directly rather than rejecting it... Dtype::I16 Some(DType::I16), Dtype::U8 Some(DType::U8), Dtype::U32 Some(DType::U32), _ None, }该函数返回None即上报OccWorldError::CheckpointParse“unsupported dtype for key …”由 src/error.rs 中定义的类型化错误体系承载。3.4 钉死回归的测试手工构造二进制容器精确到字节回归测试int32_tensor_loads_with_consistent_shape_and_values位于 tests/checkpoint_loading.rs。为了“声明测试所陈述的就是真实到达 loader 的字节”它没有依赖safetensors的 writer API而是手工拼装 SafeTensors 二进制容器u64 LE header_lenJSON headerraw data其中含一个 shape[2, 3]、6 个[1,2,3,4,5,6]的 int32 tensor。测试断言三件事恰好对应 bug 的三个后果维度shape 保持t.dims() [2, 3]元素数与 shape 一致t.elem_count() 6——旧代码上此处实际是24 bytes / 8 3存储与 shape 不一致6 vs 3dtype 与值端到端精确恢复dtype 必须是DType::I32且to_vec1::i32()还原出vec![1,2,3,4,5,6]证明两个 int32 lane 没有被重新解释成一个 int64。该文件还配套了三条防线测试组成完整的“加载健壮性测试套件”f32_tensor_round_trips—— F32 公共路径的对照用例证明修复没有回归常见浮点路径int64_tensor_round_trips—— 证明修复只收窄了 I32 映射真正的 I64 路径完好corrupt_checkpoint_errors_cleanly—— 非法字节32×0xFF必须干净地返回 parse 错误而非 panic。这些共同印证了 ADR 的验证语句checkpoint_loading 集成测试共 4 项全部通过。四、Finding #2LOWpredict边界的 frame/batch 维守卫4.1 问题只校验了空间几何漏掉了时间与批维旧版predict只对 H/W/D 三维与配置比对cfg.grid_h/grid_w/grid_d没有校验外部传入的 batch 与 frame 数。由此产生两种边界失守f_in num_frames * 2Transformer 内部的时间位置嵌入表只有num_frames*2行超容量 frame 会越界索引嵌入表在流水线深处爆出一个晦涩的 candleInvalidIndexgather 错误candle 对索引有边界检查因此是“错误”而非 panic但完全不可读、不可定位f_in 0或b 0零元素 tensor 被送进 reshape/conv 流水线。4.2 修复在系统边界处 fail closed修复后的守卫位于 src/inference.rs在拿到 5 维 shape 之后立即执行// Validate the externally-supplied frame and batch counts at this // system boundary... if f_in 0 || b 0 { return Err(OccWorldError::ShapeMismatch(format!( past_occupancy must have non-zero batch and frame dims, got \ batch{b}, frames{f_in} ))); } if f_in cfg.num_frames * 2 { return Err(OccWorldError::ShapeMismatch(format!( past_occupancy frame count {f_in} exceeds the temporal embedding \ capacity ({} num_frames*2), cfg.num_frames * 2 ))); }代码注释给出了明确的设计动机与其让深层 Transformer 暴露无法定位的 gather 越界不如在系统边界上以领域级错误ShapeMismatch一次性拒绝——这正对应项目“在系统边界校验输入”的安全规则。新增的维度守卫与既有的 H/W/D 校验同样返回ShapeMismatch形成了完整的 shape 契约。4.3 五个 pin边界值语义被精确刻画tests/input_validation.rs 以缩小配置num_frames 2→ 时间容量4刻画了完整的边界语义即 ADR 所说的“5 pins”测试输入期望predict_rejects_zero_framesbatch1, frames0ShapeMismatchpredict_rejects_zero_batchbatch0, frames2ShapeMismatchpredict_rejects_too_many_framesframes num_frames*2 1 5ShapeMismatch守卫只拒绝超容量predict_accepts_frame_count_at_capacityframes num_frames*2 4恰好等于容量成功且输出 frame 维被保留predict_rejects_wrong_grid_dimsgrid_h 1ShapeMismatch钉住既有的空间守卫值得注意第 4 条守卫不拒绝“恰好等于容量”的合法边界值——predict_accepts_frame_count_at_capacity证明加固没有误伤正常输入。五、Finding #3LOWVQCodebook::encode的除零 panic5.1 问题公开 API 上的除零VQCodebook::encode是pub struct上的pub fn属于可达的公开边界。其旧实现把输入压平成(N, last)时用z.elem_count() / last计算行数——其中last来自orig_shape.dims().last().unwrap_or(0)let last *orig_shape.dims().last().unwrap_or(0); // ❌ 旧代码last 0rank-0 标量或末维为空时直接除零 panic let n z.elem_count() / last;当输入是一个rank-0 标量 tensor空 dims 列表或末维长度为 0的 tensor 时last 0elem_count() / 0在 Rust 中直接 panic整数除零。5.2 修复fail-closed 守卫修复后的 vqvae.rs 在任何除法发生之前先拒绝退化 shape返回携带完整上下文期望的embed_dim与实际 shape的 candle 错误if last 0 { return Err(candle_core::Error::Msg(format!( VQCodebook::encode expects a tensor with a non-zero last dim of \ size embed_dim{}, got shape {orig_dims:?}, self.embed_dim ))); } // Flatten to (N, embed_dim) let n z.elem_count() / last;5.3 钉死回归同文件的单元测试encode_rejects_scalar_without_panickingvqvae.rs用Tensor::from_vec(vec![1.0f32], (), device)构造一个 rank-0 标量断言修复前last 0→ 除零 panic修复后result.is_err()干净返回绝不 panic。5.4 同类隐患的一并清理ADR 同时记录原先残留的last().unwrap_or(0)已经被上述守卫覆盖不再存在未受保护的除法路径VQCodebook本身是 VQ-VAE 中最近邻量化平方 L2||z−e_k||² ||z||² − 2·z·e_kᵀ ||e_k||²的核心组件正常路径(N, embed_dim)的往返正确性由test_vq_codebook_roundtrip另行保证。六、其余风险维度逐项“证清”cleanwith evidenceADR-179 的一个重要特点是不只是修复而是把每个曾被怀疑的风险维度都给出 grep / 测试证据后归档为 clean。这五种维度与证据如下。6.1 Panic surface生产路径零unwrap对src/全目录 grepunwrap()/expect()/panic!/unreachable!生产路径零命中所有运算都走?/map_err唯一残存的last().unwrap_or(0)已由 Finding #3 的守卫接住as类型转换只作用于配置受限/内部值。错误体系src/error.rs定义了Candle / ShapeMismatch / CheckpointNotFound / CheckpointParse / MissingKey / Io六个变体加载路径全程通过thiserror的#[from]透明转换任何 candle 内部错误都被包成OccWorldError上抛而不是让 panic 泄露。6.2 NaN-state-poisoning命名攻击类别本 crate 不适用N/A该维度对应的威胁是“NaN 权重进入有状态的 world-model 缓冲区并持续污染后续预测”。ADR 判定N/A理由有二均可由源码确认引擎在两次predict之间无状态src/inference.rs 中OccWorldCandle只持有配置与权重没有可 latch 的持久 world-model 缓冲NaN 无从“持久化”输入是u8类别索引非有限浮点输入在结构上不可能进入。即使权重本身混入 NaN它们最终流向argmax——确定性操作、结果被约束为合法类别索引——不 panic、不持久。6.3 畸形权重引发的无界分配 / shape-数据不一致由上游防线兜底这类风险在到达 candle 之前就被safetensors::validate()拒绝它执行溢出检查的nelements * dtype.size()与声明字节区间的比对、contiguous-offset 与 buffer-length 检查。corrupt_checkpoint_errors_cleanly测试验证了非法容器 →CheckpointParse错误而非 panic。而 Finding #1 之所以是 HIGH正因为它是 crate唯一一处绕过这条上游保证的地方内部 dtype 声明与文件字节实际不匹配已在第三节修复。6.4 模型/路径加载类型化错误 无路径穿越面OccWorldCandle::load与model::load_safetensors均先检查path.exists()不存在即返回类型化的CheckpointNotFound测试test_load_nonexistent_checkpoint断言该错误变体使调用方可以优雅回退到 Python bridgewifi-densepose-worldmodel加载采用安全文件读取路径std::fs::read刻意避开了 manifest 中unsafe_code forbid禁止的 unsafe mmap 块代价是整文件读入内存代码注释明确提示等 checkpoint 大到值得 mmap 时再在允许 unsafe 的 crate 中切换VarBuilder::from_mmaped_safetensors无路径穿越面路径由调用方提供、只读打开、从不与不可信段拼接。6.5 Secrets 与 Determinismgrep 干净 诚实性声明Secrets对src/grep 干净唯一命中token的只是配置字段token_h/token_wDeterminism这是本 crate 的“中心诚实性声明”central honesty claim。预置的 tests/predict_honesty.rs3 项测试在整改后依然通过覆盖输入依赖性与 run-to-run 确定性。其配套机制是 src/inference.rs 中InferenceOutput.weights_trained标志load出来的引擎置true权重来自真实训练 checkpointdummy构造器置false确定性但未训练的权重前向真实、输入相关但精度是>cargo test -p wifi-densepose-occworld-candle --no-default-features结果31/31 通过、0 失败按模块拆分正好对应前文各项测试组数量覆盖lib含单元测试17dtype 映射、key 映射、VQ 往返、config 默认值等checkpoint_loading4int32 回归 F32/I64 对照 corrupt 防御input_validation5边界守卫的 5 个 pinpredict_honesty3确定性/诚实性声明doctests2OccWorldCandle::load/OccWorldTransformer文档示例注意--no-default-features对应 Cargo.toml 中“CPU 是默认、cuda为 opt-in feature”的设计想验证 GPU 路径则需显式开启cudafeature。7.2 工作区级回归cargo test --workspace --no-default-features全部 crate 0 失败。ADR 特别记录了一个诚实归因的细节唯一的失败样例是 Windows 上wifi-densepose-desktop --test api_integration的Access is denied (os error 5)判定为文件锁/杀毒软件的 flake单独重跑后 21/21 通过与本次整改无关。7.3 信号证明路径不受影响python archive/v1/data/proof/verify.py输出VERDICT: PASS哈希f8e76f21…46f7a不变——因为 occworld 不在信号证明路径上occworld off the signal proof path加固本身不应扰动既有证明。八、结论与工程启示ADR-179 的 Consequences 部分非常简洁正收益是“world-model crate 上的检查点加载 DoSint32 dtype 位宽放大 panic与两个退化输入 panic 全部关闭并各自被测试钉死”Milestone #9全部 4 个未门控 crate至此完结负/中性影响为空——“守卫只拒绝 malformed/退化输入”不触及任何合法路径。Milestone #9 是“ungated-crate security sweep”未门控 crate 安全清扫四项分别为ruview-swarmADR-176、nvsimADR-177、desktopADR-178与本篇的 occworldADR-1794 项中的第 4 项负责收官。综合源码与测试可以把这次加固沉淀为四条可复用的工程原则dtype 映射必须字节级精确。from_raw_buffer一类 API 会依据 dtype 的size_in_bytes反推元素数任何“位宽放大式”映射都会静默构造 shape/storage 不一致的 tensor直到读取时才以晦涩的 slice-OOB panic 爆发。safetensors 的上游校验只保证“文件自洽”挡不住 crate 内部声明与文件字节不符。在系统边界校验所有外部维度。只校验“空间几何”而漏掉 frame/batch 时间维与批维等于把越界错误留到深层 transformer 里以InvalidIndex的形式暴露。边界守卫应该用领域级错误ShapeMismatch一次性拒绝零/超容输入且用“恰好等于容量必须通过”的测试证明不误伤合法值。公开 API 上除零必须 fail-closed。pub fn收到的输入不可假设 shape 合法elem_count() / last这类除法要先判定last 0返回带上下文的错误而非让调用方吞 panic。每个修复都要有一条 fails-on-old 的回归测试。ADR 中“panics on old, passes on new”的测试不仅是防回归更是把 bug 的字节级机理固化成了可执行文档——手工拼装 SafeTensors 容器、断言元素数与 shape 一致、断言 dtype 与值端到端精确还原让未来任何重构都无法在不触发测试的情况下悄悄退回旧行为。【免费下载链接】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),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。