Closed
[Bug-Report|缺陷反馈]: aclnnStack / aclnnDiag 空输入零退分支先于出参形状校验,错形 out 被 ACLNN_SUCCESS 静默放行 #3555
liuhanwen8Add on 28 days agoClosed at 22 days ago
28 days ago Added label: bug-report
陈思
28 days ago Comments:
28 days ago Comments:
您好,感谢反馈!
问题分析
核查 cann/ops-math master 8f1aae7e1927 的 Stack 与 Diag 实现,报告中的空分支确实先于输出形状校验:Stack 的单个空输入和 castIn == nullptr 两条路径均提前成功返回;Diag 的空输入且 diagonal 为 0、以及提取空对角线的分支,也不会进入后面的 CheckReduceOutShape。因此“空输入 + 错形非空 out”可能被成功放行,而不产生输出写入。
对给出的例子,Stack 将一项 [0] 在 dim=0 堆叠,预期输出为 [1,0];Diag 对一维 [0]、diagonal=0 构造对角矩阵,预期为 [0,0]。[5] 和 [5,5] 分别与这些结果不匹配,不应因输入为空而免除检查。Cat 过滤掉全部空输入后的返回,以及 DiagFlat 仅检查秩上限的入口,也存在需要分别补齐的形状约束。
处理方向
可在空分支之前根据输入、归一化后的 dim 或 diagonal 推导预期输出形状,再校验 out;形状正确的空结果才保留空执行图。这里不能只把依赖 castIn/计算结果的原检查语句前移,因为空分支尚未构造这些中间 Tensor。Diag/DiagFlat 还需保留非零 diagonal 导致非空零矩阵的语义;Cat 则需区分原始空列表与过滤后为空。我们会继续跟踪这些分支的契约补齐。


27 days ago Associated pull request
22 days ago Close issue
22 days ago Added label: resolved
Describe the current behavior / 问题描述 (Mandatory / 必填)
多个 aclnn 两段式接口把「空输入零退 / 空跳过」分支放在出参形状校验之前,导致 out 形状错误(甚至非空)时接口仍返回
ACLNN_SUCCESS且不写 out,调用方拿到陈旧/未初始化缓冲。代表位置:
conversion/pack/op_api/aclnn_stack.cpp(aclnnStackGetWorkspaceSize,当前 master)——CheckParams只校验输入(CheckShape(tensors, realDim)不含 out),出参校验在 L533,而两条零退在 L513-517 / L529-531:513: if (tensors->Size() == 1) { 514: if ((*tensors)[0]->IsEmpty()) { 515: *workspaceSize = 0; 516: uniqueExecutor.ReleaseTo(executor); 517: return ACLNN_SUCCESS; // ← 出参形状校验被跳过 ... 529: if (castIn == nullptr) { *workspaceSize = 0; ...; return ACLNN_SUCCESS; } ... 533: CHECK_RET(CheckShapeAndScalarSame(castIn, out), ACLNN_ERR_PARAM_INVALID);conversion/diag_v2/op_api/aclnn_diag.cpp(aclnnDiagGetWorkspaceSize)——CheckShape(self)仅校验 self(L70,经 CheckParams L123),零退在 L142-145,其前无任何出参形状校验:142: if ((self->IsEmpty() && diagonal == 0) || CheckDiagonal(self, diagonal)) { 143: *workspaceSize = 0; 144: uniqueExecutor.ReleaseTo(executor); 145: return ACLNN_SUCCESS; // ← 出参形状校验被跳过 146: }同族附注:
conversion/concat_d/op_api/aclnn_cat.cpp:L536-538if (tensorListA.empty()) { return ACLNN_SUCCESS; }先于 L539CheckShapeAndScalarSame(tensorListA.front(), out)。conversion/diag_flat/op_api/aclnn_diag_flat.cpp:CheckShape(self, out)仅做OP_CHECK_MAX_DIM(秩上限),不比较形状,零退在 L135-146(属"从未校验 out 形状"而非"次序颠倒")。对照正确范式:
math/abs/op_api/aclnn_abs.cpp在CheckParams中前置CheckShape(self, out)→OP_CHECK_SHAPE_NOT_EQUAL(self, out),且入口先CheckParams再空分支,故无此问题。Environment Information / 环境信息 (Mandatory / 必填)
Steps to reproduce the issue / 重现步骤 (Mandatory / 必填)
以
aclnnStackGetWorkspaceSize/aclnnDiagGetWorkspaceSize为例,构造「空输入 + 错形 out」,并与「非空输入 + 同一错形 out」对照:#!/usr/bin/env python3 import ctypes as C LIB = "<CANN>/aarch64-linux/lib64" cl = C.CDLL(f"{LIB}/libascendcl.so", mode=C.RTLD_GLOBAL) op = C.CDLL(f"{LIB}/libopapi.so", mode=C.RTLD_GLOBAL) cl.aclInit.argtypes=[C.c_void_p]; cl.aclrtSetDevice.argtypes=[C.c_int32] cl.aclrtCreateStream.argtypes=[C.POINTER(C.c_void_p)] cl.aclrtMalloc.argtypes=[C.POINTER(C.c_void_p), C.c_size_t, C.c_int32] op.aclCreateTensor.argtypes=[C.POINTER(C.c_int64), C.c_uint64, C.c_int32, C.POINTER(C.c_int64), C.c_int64, C.c_int32, C.POINTER(C.c_int64), C.c_uint64, C.c_void_p] op.aclCreateTensor.restype=C.c_void_p op.aclCreateTensorList.argtypes=[C.POINTER(C.c_void_p), C.c_uint64] op.aclCreateTensorList.restype=C.c_void_p P=C.c_void_p; FLOAT=0; ND=2 cl.aclInit(None); cl.aclrtSetDevice(1) st=P(); cl.aclrtCreateStream(C.byref(st)) def mk(dims): n=len(dims); dd=(C.c_int64*max(n,1))(*dims); ss=(C.c_int64*max(n,1))(*([1]*n)) p=P(); cl.aclrtMalloc(C.byref(p),256,0) return op.aclCreateTensor(dd,n,FLOAT,ss,0,ND,dd,n,p) def tl(dims): return op.aclCreateTensorList((P*1)(mk(dims)),1) ST=op.aclnnStackGetWorkspaceSize; ST.restype=C.c_int ST.argtypes=[P,C.c_int64,P,C.POINTER(C.c_uint64),C.POINTER(P)] DG=op.aclnnDiagGetWorkspaceSize; DG.restype=C.c_int DG.argtypes=[P,C.c_int64,P,C.POINTER(C.c_uint64),C.POINTER(P)] def run(fn,args,tag): ws=C.c_uint64(0); ex=P(); r=fn(*args, C.byref(ws), C.byref(ex)) print(f"{tag:34s} ret={r} (0=SUCCESS 放行, 161002=PARAM_INVALID 已拒绝)") run(ST,[tl([0]),0,mk([5])], "stack 空输入[0] + 错形out[5]") run(ST,[tl([3]),0,mk([5])], "stack 非空[3] + 错形out[5]") run(DG,[mk([0]),0,mk([5,5])], "diag 空输入[0] + 错形out[5,5]") run(DG,[mk([3]),0,mk([5,5])], "diag 非空[3] + 错形out[5,5]")运行:
source <CANN>/set_env.sh && python3 repro.pyDescribe the expected behavior / 预期结果 (Mandatory / 必填)
ACLNN_ERR_PARAM_INVALID,不得返回 SUCCESS;CheckParams),对齐aclnn_abs.cpp的既有正确范式。Related log / screenshot / 日志 / 截图 (Mandatory / 必填)
实测(Atlas 800T A2 / CANN 9.2.0-beta.2):
aclnnStackret=0(SUCCESS),且 out 保持预填值 1234.0 未写ret=161002aclnnDiagret=0(SUCCESS)ret=161002对照
aclnnAbs:self=[0] + out=[5] → 161002(已在CheckParams前置OP_CHECK_SHAPE_NOT_EQUAL,无此问题)。Special notes for this issue/备注 (Optional / 选填)
*workspaceSize)——三者同属"零退分支契约不完整",但本单点位是「出参形状校验被零退跳过」,修复方式(把形状校验前移/补齐)与 #3553(回填 0)、#3554(补 workspaceSize)不同,非重复。aclnn_stack.cppL533 之前的零退补形状校验(或前移);aclnn_diag.cpp在 L142 前补 out 形状校验;aclnn_cat.cppL536 与aclnn_diag_flat.cppL135 同族一并排查。