Open
【缺陷】BatchNorm3DGrad 多用例 UB 越界读(illegal read size 236/304) #227
xieyangyangAdd on  3 days ago
xieyangyangCollaborators
3 days ago Create

【缺陷】BatchNorm3DGrad 多用例 UB 越界读(illegal read size 236/304)

问题描述信息

【环境信息】:

  • 设备信息:Ascend950PR_957b(驱动 9.1.t13.0.b130 / 固件 20260826)
  • CANN信息:CANN 9.3.0(20261110)
  • torch环境信息:torch 2.14.0.dev20260811 / torch_npu 2.14.0.dev20261008
  • 测试框架:TTK 3.0.0 / mssanitizer 26.3.0

【预置条件】:

  • 算子包部署:bash build.sh --jit --soc=ascend950 --ops=batchnorm3dgrad
  • 插桩编译:--compile-opts op_debug_config=sanitizer
  • mssanitizer wrap 执行

【重现步骤】:

  1. 构建算子包(含插桩):
    cd <ops仓> && bash build.sh --jit --soc=ascend950 --ops=batchnorm3dgrad
  2. mssanitizer 包裹执行 TTK kernel 模式:
    mssanitizer --tool=memcheck --leak-check=yes --check-unused-memory=yes --
    /npu/miniconda3/envs/CI_py310_aclnn/bin/python3 -m ttk kernel
    -i batchnorm3dgrad_cases.csv --compile-opts op_debug_config=sanitizer
    --pc 1 --devices 0

【预期结果】:
无 mSAN ERROR(无越界读写、无内存泄漏)

【实际结果】:
mssanitizer memcheck 检出 9 条 ERROR(illegal read of size 236×3 + size 304×6),全部为 UB 越界读。触发 kernel 为 dyn_op_BatchNorm3DGrad_L0_001(block aiv(0-46), pc 0x331b8)和 dyn_op_BatchNorm3DGrad_L0_011(block aiv(0-11), pc 0x39a5c)。

【用户影响】:
多用例 UB 越界读——不同 shape 均触发,可能为系统性问题

附件


责任田:张磊(目标仓:wangyueting/changwei)

likedislike
Sun
Sun
1 day ago Comments:

非真实问题

报错可稳定复现,次数、size、block、地址均与 issue 完全一致。经核查,属mssanitizer 对 DataCopyPad(..., PaddingMode::Compact) 取数地址的建模偏差,不是算子真实越界,也不影响精度。

1. 复现

  • 环境:CANN 9.3.0(/home/LuckySun/Ascend)、Ascend950DT_9592(72 AIV)、TTK 3.0.0、mssanitizer 26.3.0.dev202611120100
mssanitizer --tool=memcheck --leak-check=yes --check-unused-memory=yes -- \
  python3 -m ttk kernel -i <附件 trigger_cases.csv> \
  --compile-opts op_debug_config=sanitizer --pc 1 --devices 0
用例 结果
L0_001(x=(14,47,45,15) fp32) L0_001_10000000,block aiv(0-46):3 × illegal read of size 236
L0_011(x=(54,12,59,12) fp32) L0_011_31000000,block aiv(0-11):6 × illegal read of size 304
L0_001_varp10 / L0_001_varm146 / L0_011_varp110 附件行本身非法(x 与 dy 形状不一致)→ Input shapes are invalid.,tiling 失败,非算子问题

合计 9 条 illegal read,地址 0x1b1a0、0x28de0、0x36b20,与 issue 日志逐地址一致;
records undetected = 0(覆盖完整,非漏扫)。

2. 根因

一句话:工具以为每行数据之间有 32B 对齐填充,按"加大后的行距"算读地址,于是算到了缓冲区外面;实际数据是紧挨着放的。

分四步:

  1. 搬运方式:dx 由 CopyOutRAR() 从 UB 搬回 GM ——
    op_kernel/arch35/batch_norm3d_grad_full_load_regbase.h:615(tiling key 10000000)与
    batch_norm3d_grad_split_r1_regbase.h:1098(tiling key 31000000)的
    DataCopyPad<..., PaddingMode::Compact>,一次搬 r1 行(14 行),每行 = r0 × 4B
    = 2700 B(r0=675)或 2832 B(r0=708)。UB 内行与行紧挨着,无填充;
    缓冲按紧凑总长分配:14×2700 = 37800 B ≤ 37824 B、14×2832 = 39648 B ≤ 39680 B。

  2. 工具怎么算:mssanitizer 把行距按 32 B 向上取整(2700 → 2720、2832 → 2848),
    第 14 行起点算成 13×2720 = 35360 B,读 2700 B 到 38060 B,比 37824 B 的缓冲多出 236 B
    —— 这与 issue 报的 size 236 精确吻合(另一条路径多出 176 B,对应 size 304 的同类量)。

  3. 实际怎么取数:CANN 实现里,Compact 模式的行步长直接用行字节数,不做 32 B 对齐;
    只有 Normal 模式才对齐(asc/impl/basic_api/dav_3510/kernel_operator_data_copy_impl.h:102-123):

if constexpr (mode == PaddingMode::Normal) {
    srcStride310 = srcStride * 32 + burstLength;
    srcStride310 = DivCeil(srcStride310, 32) * 32;   // 只有 Normal 强制 32B 对齐
} else {
    srcStride310 = burstLength;                       // Compact:字节精确、忽略 srcStride
}

即第 14 行实际起点 13×2700 = 35100 B,读 2700 B 到 37800 B,仍在缓冲区内。

  1. 旁证:若真按 2720 步进,dx 从第 2 行起就会整体错位,精度必然失败;而该用例带 golden 的
    对照轮次精度为 PASS,PASS,PASS,100%,100%。
likedislike