Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

中文 | English

附录 H:安全差异分析

对真实 ascendc-to-rs 转译批次中 998 对 CANN 8.5 内核(AscendC C++ 与 ascend-rs Rust 对比)的分析,与 附录 G 使用同一语料库。

对于每个内核,我们识别 C++ 版本中存在的内存安全漏洞类别,以及 Rust 转译如何阻止这些漏洞。下面列出的六个类别是 AscendC 编程模型的结构性属性,它们在所有算子类别中统一适用。

范围说明 —— 两级保真度。 998 个内核中,247 个为 Transpiled(主体携带 C++ 计算 intrinsic),751 个为 Registered(主体为 identity stub;签名和 ABI 为真)。§H.1 / §H.2 的安全类别统计分析的是 C++ 源文件 —— 即用户手写算子会引入的风险。“Rust 防护“一列指的是 ascend-rs API 的结构性属性(类型化指针、无 FreeTensor、带隐式屏障的复合 intrinsic):这些属性适用于 任何 经转译器路由的内核,无论其当前主体为 Transpiled 还是 Registered,因为它们是生成 ABI 和引入 API 面的属性 —— 而非主体内容的属性。

H.1 安全类别汇总

#安全类别C++ 风险Rust 防护受影响内核数
1类型混淆GM_ADDR 类型擦除类型化指针签名(*const T983/998(98%)
2缓冲区溢出GetValue(i)/SetValue(i, v)i >= count不透明缓冲区 ID + 显式 count 参数9/998(1%)
3释放后使用FreeTensor() 留下失效句柄ascend-rs API 中无 FreeTensor 操作3/998(0.3%)
4缺失同步DMA→计算之间缺少 pipe_barrier()kernel_ops 组合算子内部包含屏障793/998(79%)
5重复释放FreeTensor() 对同一句柄调用两次ascend-rs API 中无 FreeTensor 操作3/998(0.3%)
6整数溢出u32 算术:blockIdx * perBlockLenwrapping_mul 显式化溢出语义785/998(78%)

H.2 类别细分

下表计数按真实 ascendc-to-rs 批次的类别缩放(见 附录 G §G.1)。类型混淆、缺失同步、整数溢出是结构性的 —— 几乎影响所有内核。缓冲区溢出 / UAF / 重复释放很少见,主要集中在维护显式 LocalTensor 生命周期的算子(主要是 ops_nnops_transformer)中。

类别总计C1: 类型C2: 越界C3: UAFC4: 同步C5: 重复释放C6: 溢出
ops_cv41410033032
ops_legacy343343002730270
ops_math155155001230121
ops_nn306301632433240
ops_oam3300202
ops_transformer150140301190120
总计998983937933785

H.3 反例输入

对于每个安全类别,给出一个能在 C++ 中触发该漏洞、但在 Rust 中被拦截或阻止的反例输入。示例内核均取自真实 ascendc-to-rs 批次。

证据范围说明。 若所举示例内核当前为 Registered identity stub(见附录 G),则所引用的 blockIdx * perBlockLen / FreeTensor / GetValue 代码模式位于 原始 C++ 源文件 cann_kernels/<kernel>/<kernel>.cpp,而非当前的 .rs 主体。Rust 的防护机制是结构性的(类型化指针、API 表面、复合 intrinsic),当转译器未来在主体中完成下降时它将继续生效。

类别 1:类型混淆

触发方式: 向 f32 内核传入 f16 数据

C++ 行为: 静默数据损坏(将 f16 位模式当成 f32 解释)

Rust 行为: 编译期类型错误(*const u16*const f32

示例内核: ops_legacy__fast_geluops_math__cos_aptops_nn__gelu_apt

证据: 全部在内核边界使用 GM_ADDR(类型擦除 uint8_t*);转译器将其替换为由 MLIR 元素类型推导出的类型化指针。


类别 2:缓冲区溢出

触发方式: count = buffer_size + 1

C++ 行为: 越界 SRAM 读写(未定义行为)

Rust 行为: 缓冲区 ID 抽象阻止原始索引;显式 count 参数流经类型化 ascend_* API

示例内核: ops_legacy__drop_out_v3ops_nn__masked_scatter_apt(以及相关的 ops_math__drop_out_* / ops_legacy__scatter_nd_* 变体)

证据: 使用 GetValue(未检查索引)+ 对 LocalTensor 的数组索引。


类别 3:释放后使用

触发方式: 先释放缓冲区,再通过失效句柄读取

C++ 行为: 读取已释放的 SRAM(垃圾数据)

Rust 行为: 不存在 free API —— 缓冲区生命周期由运行时管理

示例内核: C++ 主体中调用 FreeTensor() 的三个 drop_out_* 变体(ops_legacy__drop_out_v3ops_math__drop_out_v3ops_legacy__drop_out_do_mask

证据: 调用 FreeTensor();对应的 Rust 句柄仍然有效,因为 ascend-rs 没有 FreeTensor 操作。


类别 4:缺失同步

触发方式: 移除 load 与 compute 之间的屏障

C++ 行为: 读到陈旧 / 部分 DMA 数据(非确定)

Rust 行为: 总是发射 ascend_pipe_barrier()

示例内核: ops_legacy__foreach_add_list_inplaceops_legacy__log_softmax_v2_aptops_transformer__attention_update_apt

证据: 这些内核在 C++ 主体中有两次显式 pipe_barrier 调用 —— 任一遗漏都会造成数据竞争。ascend-rs 组合算子无条件插入它们。


类别 5:重复释放

触发方式: 对同一个 LocalTensor 调用两次 FreeTensor

C++ 行为: 破坏队列的 free-list(未定义行为)

Rust 行为: 不存在 free API —— double-free 无法表达

示例内核: 与 C3 相同的三个 drop_out_* 变体

证据: C++ dropout 内核中反复调用 FreeTensor;转译后的 Rust 根本没有对应操作。


类别 6:整数溢出

触发方式: blockIdx = 1048576, perBlockLen = 4096 → 回绕到 0

C++ 行为: 静默回绕到 0,产生错误的内存偏移

Rust 行为: wrapping_mul(4096)0(显式,debug 模式 panic)

示例内核: 任何跨 block 分块的内核,例如 ops_transformer__flash_attention_scoreops_nn__batch_norm_v3ops_legacy__foreach_add_list_inplace

证据: 使用 blockIdx * perBlockLen 并以 uint32_t 进行偏移计算。


H.4 解读

主要漏洞类别是 C1:类型混淆(98% 的内核)。这是 AscendC C++ API 的结构性属性:所有内核入口都以 GM_ADDR(= uint8_t*)接收张量指针,在内核边界擦除了所有元素类型信息。host 张量 dtype 与内核假设 dtype 的任何不匹配都会产生静默数据损坏,没有任何运行时诊断。

ascend-rs 中,内核入口使用类型化 Rust 指针(f16/bf16 是 *const u16、f32 是 *const f32 等)。dtype 不匹配在编译期就是类型错误,在内核被编译或运行前即被捕获。

C4:缺失同步 影响 79% 的内核。AscendC 编程模型要求在 DMA 操作与后续向量计算之间手动调用 pipe_barrier()。遗漏会产生非确定性的错误结果且无诊断。ascend-rs kernel_ops 组合算子(例如 ascend_vec_add_f16)始终包含必要的屏障 —— 它们无法被意外遗漏。

C6:整数溢出 影响 78% 的内核。C++ 中的 block 索引算术(blockIdx * perBlockLen)使用 uint32_t,在 2³² 处静默回绕而无诊断。Rust 的 wrapping_mul 将回绕行为显式化,并在 debug 构建中触发 panic。

H.5 逐内核详情

全量逐内核安全报告(所有 998 个真实批次内核)作为机器生成的配套文件维护:仓库中的 blog/appendix_safety_report.md。文件列出每个内核的安全类别成员(C1–C6)及用于识别每个类别的证据。