已关闭
[Bug-Report|缺陷反馈]: aclnnThreeInterpolateBackward A2 kernel 对 idx/weight 搬运长度未夹紧,合法输入即越界读(memcheck 16 处,96→180B) #1406
liuhanwen8创建于  12 天前关闭于  6 天前
liuhanwen8
liuhanwen8
12 天前 创建

现象

在完全合法的输入下(idx 取值全部落在 [0, m)),msSanitizer memcheck 在算子 kernel
ThreeInterpolateBackward_fp32_int32_high_performance_0 上检出 16 处 illegal_read。

用例:grad_x=fp32[2x3x4x1]NCHW、idx=int32[2x4x3]ND、weight=fp32[2x4x3]ND、m=6、grad_y=fp32[2x3x6x1]NCHW

illegal read of size 96  | at 0x12c0c001a000 on GM in ThreeInterpolateBackward_fp32_int32_high_performance_0
illegal read of size 96  | at 0x12c0c001b000 on GM in ThreeInterpolateBackward_fp32_int32_high_performance_0
illegal read of size 108 / 120 / 132 / 144 / 156 / 168 / 180 | 同上两个基址 +12 / +24 / +36 / +48 / +54
  • 两个基址分别对应 idx(24 × int32)与 weight(24 × fp32),均为 96 B;
    实际读长度 96 → 180 B(步长 12 B = 3 个元素),最多超出缓冲区 84 B;
  • 同一次 memcheck 中框架 kernel(OnesLike / MatMulV2 / TransData)只有寄存器复位警告,无 illegal_read,
    故定位在算子 kernel;
  • 另一用例 grad_x=fp32[1x1x1x1]NCHW、idx=(1,1,3)、grad_y=(1,1,4,1) 另报 2 处(同 kernel)。

复现

msSanitizer(memcheck)单用例直调:
  grad_x=fp32[2x3x4x1]NCHW | idx=int32[2x4x3]ND(取值全在 [0,6)) | weight=fp32[2x4x3]ND
  m=6 | grad_y=fp32[2x3x6x1]NCHW
→ memcheck:16 处 illegal_read,位置全部落在 ThreeInterpolateBackward_fp32_int32_high_performance_0

期望

idx / weight 的搬运长度按实际张量大小夹紧(疑似 tiling 中 each_core_proc_num / n 的上界未夹紧),
消除对输入缓冲区的越界读。

备注

同批 initcheck 在同一 kernel 另报未初始化读(UB 672 B、GM 96 B@0x12c0c001a000 等,共 27 处),
推测与同一处长度计算相关,建议一并排查。

likedislike
liu-wei
liu-wei成员
12 天前 评论:

您好,感谢反馈!

经过分析,问题属实:A2 kernel 对 idx/weight 的搬运长度固定为 N0 * 3 个元素,未按末循环实际 n 数量夹紧,单核 n 余量不足 N0=16 时即对输入张量产生越界读,与 memcheck 检出结果一致。

问题分析

问题位于 CopyIn(three_interpolate_backward.cpp#L261-L262):

DataCopy(idx_local, idx_gm[idx_addr_offset], N0 * 3);
DataCopy(weight_local, weight_gm[weight_addr_offset], N0 * 3);
  • 搬运长度固定为 N0 * 3 = 48 个元素。对比同一函数中 grad_x 的搬运(blockLen 取 copy_in_last_loop_block_size,由 core_last_loop_n_cnt 按末循环 n 数夹紧,见 L140),idx/weight 没有对应的夹紧逻辑;tiling 侧 idx_move_block_size/weight_move_block_size 也按满额 3 * N0 计算(three_interpolate_backward_tiling.cpp#L118-L119),UB 空间按满额分配,搬运会完整执行;
  • 以您的用例(idx=int32[2x4x3],共 24 个元素 96 B)为例:n=4 < N0=16,各核仍按 48 个元素搬运,读取范围越过 96 B 的缓冲区,与 memcheck 在两个 96 B 基址上报出的 illegal_read 相符;
  • 越界读到的数据不参与有效计算(计算与写出环节均按 core_last_loop_n_cnt 截断取值),因此计算结果正确,但构成对输入缓冲区的非法读,与您"合法输入、结果正常但 memcheck 报错"的观察一致。

后续计划

我们计划将 idx/weight 的搬运长度按末循环 n 数夹紧(对齐 32 B),消除对输入缓冲区的越界读;备注中提到的同 kernel initcheck 未初始化读问题,会在修复时一并排查确认是否同源。修复与回归用例将走评审流程合入,合入后会在本 issue 同步进展。感谢您提供完整的 memcheck 信息。

likedislike
Ssunhao_hw成员
12 天前 将 sunhao_hw 设为负责人
liuhanwen8
liuhanwen8
8 天前 评论:

补充:A2 上可复现的最小脚本(NCHW 形态,memcheck 16 处 illegal read)

环境:Atlas A2(910B2,NPU4 / Health=OK)、CANN 9.2.0、g++ 11.4.0、aclnn 原生两段式直调(无 torch)。
ASCEND_RT_VISIBLE_DEVICES=4,单卡单进程。
注意:grad_x / grad_y 必须按 NCHW 创建;用 ACL_FORMAT_ND 时首段直接返回 561103。

#include <cstdio>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_three_interpolate_backward.h"
#define CK(x) do{auto _r=(x); if(_r!=0){printf("FAIL L%d ret=%d\n",__LINE__,(int)_r);return 2;}}while(0)
static aclTensor* mk(std::vector<int64_t> s, aclDataType d, void* p, aclFormat f){
    std::vector<int64_t> st(s.size(),1);
    for(int i=(int)s.size()-2;i>=0;--i) st[i]=s[i+1]*st[i+1];
    return aclCreateTensor(s.data(),s.size(),d,st.data(),0,f,s.data(),s.size(),p);
}
static int run(const char* tag,int B,int Cc,int N,int M,int m,aclFormat f){
    std::vector<float> hx(B*Cc*N,1.f), hw(B*N*M,0.3f);
    std::vector<int32_t> hi(B*N*M);
    for(size_t i=0;i<hi.size();++i) hi[i]=(int32_t)(i%m);
    void *dx,*di,*dw,*dy;
    CK(aclrtMalloc(&dx,B*Cc*N*4,ACL_MEM_MALLOC_HUGE_FIRST));
    CK(aclrtMalloc(&di,B*N*M*4,ACL_MEM_MALLOC_HUGE_FIRST));
    CK(aclrtMalloc(&dw,B*N*M*4,ACL_MEM_MALLOC_HUGE_FIRST));
    CK(aclrtMalloc(&dy,B*Cc*m*4,ACL_MEM_MALLOC_HUGE_FIRST));
    CK(aclrtMemcpy(dx,B*Cc*N*4,hx.data(),B*Cc*N*4,ACL_MEMCPY_HOST_TO_DEVICE));
    CK(aclrtMemcpy(di,B*N*M*4,hi.data(),B*N*M*4,ACL_MEMCPY_HOST_TO_DEVICE));
    CK(aclrtMemcpy(dw,B*N*M*4,hw.data(),B*N*M*4,ACL_MEMCPY_HOST_TO_DEVICE));
    aclTensor *tx=mk({B,Cc,N,1},ACL_FLOAT,dx,f), *ti=mk({B,N,M},ACL_INT32,di,ACL_FORMAT_ND),
              *tw=mk({B,N,M},ACL_FLOAT,dw,ACL_FORMAT_ND), *ty=mk({B,Cc,m,1},ACL_FLOAT,dy,f);
    uint64_t ws=0; aclOpExecutor* ex=nullptr;
    aclnnStatus r1=aclnnThreeInterpolateBackwardGetWorkspaceSize(tx,ti,tw,m,ty,&ws,&ex);
    void* wp=nullptr; if(ws) CK(aclrtMalloc(&wp,ws,ACL_MEM_MALLOC_HUGE_FIRST));
    aclrtStream st=nullptr; CK(aclrtCreateStream(&st));
    aclnnStatus r2=(r1==0)?aclnnThreeInterpolateBackward(wp,ws,ex,st):(aclnnStatus)-1;
    if(r2==0) CK(aclrtSynchronizeStream(st));
    printf("TI[%s] B=%d C=%d N=%d M=%d m=%d ret1=%d ret2=%d ws=%llu\n",
           tag,B,Cc,N,M,m,(int)r1,(int)r2,(unsigned long long)ws);
    return 0;
}
int main(){
    CK(aclInit(nullptr)); CK(aclrtSetDevice(0));
    run("ND",2,3,4,3,6,ACL_FORMAT_ND);
    run("NCHW",2,3,4,3,6,ACL_FORMAT_NCHW);
    run("ND_small",1,1,1,3,4,ACL_FORMAT_ND);
    return 0;
}

编译与运行

g++ -O2 repro.cpp -o repro -I$ASCEND_HOME_PATH/include -L$ASCEND_HOME_PATH/lib64 \
    -lopapi -lascendcl -lnnopbase -lpthread
ASCEND_RT_VISIBLE_DEVICES=4 ./repro

mssanitizer -t memcheck --log-file mem.log -- ./repro
grep -c illegal mem.log

实测输出(本次复跑)

TI[ND]    B=2 C=3 N=4 M=3 m=6 ret1=561103 ret2=-1 ws=0
TI[NCHW]  B=2 C=3 N=4 M=3 m=6 ret1=0 ret2=0 ws=16779776
TI[ND_small] B=1 C=1 N=1 M=3 m=4 ret1=561103 ret2=-1 ws=0
(memcheck) illegal = 16, 全部落在 ThreeInterpolateBackward_fp32_int32_high_performance_0

结论:合法输入(idx 全部 ∈ [0,6))下两段 0/0,memcheck 检出 16 处 illegal read,全部在算子 kernel,与主体完全一致。

likedislike
Zzhuzemao成员
6 天前 关联了pull request:[修复] ThreeInterpolateBackward 文档及越界问题修复
Zzhuzemao成员
6 天前 将 zhuzemao 设为负责人
Zzhuzemao成员
6 天前 移除了负责人 sunhao_hw
CANN-robotCANN-robot成员
6 天前 关闭了 issue
CANN-robotCANN-robot成员
6 天前 添加了label:resolved
Ssunhao_hw成员
5 天前 issue状态由 进行中 改变为 已完成