Closed
[Bug-Report|缺陷反馈]: aclnnSparseFlashAttention value 传空指针返回 0,未按资料返回 161001 #6637
liuhanwen8Add on 20 days agoClosed at 14 days ago
20 days ago Added label: bug-report
周云飞
20 days ago Comments:
20 days ago Comments:
感谢提供详细的复现参数和逐项置空对照。
从您提供的 CANN 9.2.0、Atlas A2(910B2)测试结果看,仅将 value 置为 nullptr 时,aclnnSparseFlashAttentionGetWorkspaceSize 仍返回 0,这与您引用的必选参数空指针应返回 ACLNN_ERR_PARAM_NULLPTR(161001) 的说明不一致。这里需要区分空指针与空 tensor:本次报告的是 value 指针本身为空,应重点检查必选输入的空指针校验及其错误码返回路径。
目前的代码片段尚未覆盖完整的参数检查和内部调用实现,因此还不能确定是遗漏了 value 判空,还是检查失败后未正确向外返回错误码。另外,执行段返回 0 本身不足以证明 kernel 已实际访问空指针,但也不能据此认为该输入可以安全使用。
一个可行的处理方向是:在 GetWorkspaceSize 阶段、任何访问或向下传递 value 的操作之前,确保空指针被拦截,并向调用方返回 161001。您建议的“仅 value 为空、其余参数保持有效”的单测很有针对性,可以用于验证这一行为,同时保留正常输入的成功路径。使用侧可暂时在调用前检查 value,为空时直接拒绝调用,不再进入执行段。我们会继续确认具体校验路径与返回码传递情况。


20 days ago Modify description of issue
周云飞
18 days ago Comments:
18 days ago Comments:
/assign @gcw_oeok7yxb


18 days ago Assign gcw_oeok7yxb as responsible person
16 days ago Associated pull request
14 days ago Close issue
14 days ago Added label: resolved
Describe the current behavior / 问题描述 (Mandatory / 必填)
必选输入
value传空指针时,两段式接口都返回 0;同签名的query传空指针正确返回 161001。资料错误码表写明「必选输入/输出/属性为空指针 → 161001」,value是必选输入。value = nullptrquery = nullptr(对照)Environment Information / 环境信息 (Mandatory / 必填)
$ASCEND_HOME_PATH指向 CANN)Steps to reproduce the issue / 重现步骤 (Mandatory / 必填)
repro.cpp并按顶部命令编译运行。value=nullptr、query=nullptr三次调用,其余参数完全相同。value=nullptr被放行。Describe the expected behavior / 预期结果 (Mandatory / 必填)
value == nullptr时aclnnSparseFlashAttentionGetWorkspaceSize应返回 161001,与 query/key/sparseIndices 等必选输入一致,不应进入执行段。Related log / screenshot / 日志 / 截图 (Mandatory / 必填)
Special notes for this issue/备注 (Optional / 选填)
复现脚本(单文件自包含,50 行,含对照组与缺陷组)
g++ -O2 -I$ASCEND_HOME_PATH/include -I$ASCEND_HOME_PATH/include/aclnnop repro.cpp \ -L$ASCEND_HOME_PATH/lib64 -lopapi_transformer -lopapi -lnnopbase -lascendcl \ -Wl,-rpath,$ASCEND_HOME_PATH/lib64 -o repro && ./repro// aclnnSparseFlashAttention: value=nullptr 两段均返回 0(资料要求 161001) // g++ -O2 -I$ASCEND_HOME_PATH/include -I$ASCEND_HOME_PATH/include/aclnnop repro.cpp \ // -L$ASCEND_HOME_PATH/lib64 -lopapi_transformer -lopapi -lnnopbase -lascendcl -o repro #include <acl/acl.h> #include <aclnnop/aclnn_sparse_flash_attention.h> #include <cstdio> #include <cstdint> #include <cmath> #include <vector> typedef std::vector<int64_t> S; static void *M(size_t n){void*p=nullptr;aclrtMalloc(&p,n,ACL_MEM_MALLOC_HUGE_FIRST);return p;} static aclTensor*T(S s,aclDataType d,void*p){static std::vector<S> k;k.push_back(s); auto&v=k.back();return aclCreateTensor(v.data(),v.size(),d,0,0,ACL_FORMAT_ND,v.data(),v.size(),p);} static void F16(void*p,size_t n){std::vector<uint16_t>h(n,0x3C00); aclrtMemcpy(p,n*2,h.data(),n*2,ACL_MEMCPY_HOST_TO_DEVICE);} static void I32(void*p,std::vector<int32_t>v){ aclrtMemcpy(p,v.size()*4,v.data(),v.size()*4,ACL_MEMCPY_HOST_TO_DEVICE);} // nullWhich: 0=基线, 1=value, 2=query static int one(int nullWhich,int*p2){ const int64_t B=1,S1=2,N1=1,S2=64,D=512,Dr=64; void*q=M(B*S1*N1*D*2),*k=M(B*S2*D*2),*v=M(B*S2*D*2); void*si=M(B*S1*4*4),*aq=M(4),*ak=M(4); void*qr=M(B*S1*N1*Dr*2),*kr=M(B*S2*Dr*2); void*oa=M(B*S1*N1*D*2),*om=M(B*S1*N1*4),*os=M(B*S1*N1*4); F16(q,B*S1*N1*D);F16(k,B*S2*D);F16(v,B*S2*D);F16(qr,B*S1*N1*Dr);F16(kr,B*S2*Dr); I32(si,{0,1,2,3});I32(aq,{S1});I32(ak,{S2}); uint64_t ws=0;aclOpExecutor*ex=nullptr; int rc=(int)aclnnSparseFlashAttentionGetWorkspaceSize( nullWhich==2?nullptr:T({B,S1,N1,D},ACL_FLOAT16,q), T({B,S2,1,D},ACL_FLOAT16,k),nullWhich==1?nullptr:T({B,S2,1,D},ACL_FLOAT16,v), T({B,S1,1,4},ACL_INT32,si),nullptr,T({1},ACL_INT32,aq),T({1},ACL_INT32,ak), T({B,S1,N1,Dr},ACL_FLOAT16,qr),T({B,S2,1,Dr},ACL_FLOAT16,kr), 1.0/std::sqrt((double)(D+Dr)),16,(char*)"BSND",(char*)"BSND", 0,INT64_MAX,INT64_MAX,2,false, T({B,S1,N1,D},ACL_FLOAT16,oa),T({B,1,S1,N1},ACL_FLOAT,om), T({B,1,S1,N1},ACL_FLOAT,os),&ws,&ex); *p2=-1; if(rc==0)*p2=(int)aclnnSparseFlashAttention(ws?M(ws):nullptr,ws,ex,nullptr); return rc; } int main(){ aclInit(nullptr);aclrtSetDevice(0); int a2=0,b2=0,c2=0; int a=one(0,&a2),b=one(1,&b2),c=one(2,&c2); printf("baseline : phase1=%d phase2=%d\n",a,a2); printf("value=nullptr : phase1=%d phase2=%d (期望 161001)\n",b,b2); printf("query=nullptr : phase1=%d phase2=%d (对照)\n",c,c2); aclrtResetDevice(0);aclFinalize();return 0; }影响:放行后 kernel 会带着空指针进入计算(读到非法地址或静默读到零页),属于稳定性风险,且上层无法在调用前发现参数错误。
建议:在参数校验入口补
value判空,并补一条「仅 value 为空」的 UT(现有用例多为单参数置空,容易漏)。