Closed
[Bug-Report|缺陷反馈]: aclnnUnfoldGrad 空指针返回 561103 而非文档承诺的 161001,且 len(inputSizes)=3 时 dim=0 未被拒绝 #3795
liuhanwen8Add on  21 days agoClosed at  16 days ago
liuhanwen8
liuhanwen8
21 days ago Create

问题描述

aclnnUnfoldGrad 存在两处与文档契约直接冲突的校验缺陷,且都在第一段接口即可复现:

编号 现象 文档要求 实测
UG-02 gradOut / inputSizes / gradIn 任一传 nullptr 161001 561103(ACLNN_ERR_INNER_NULLPTR)
UG-03 len(inputSizes)=3 时传 dim=0 161002 0(SUCCESS,被接受)

契约依据(conversion/unfold_grad/docs/aclnnUnfoldGrad.md):

行 内容
:127 「dim 仅支持 len(inputSizes)-1 或者 len(inputSizes)-2。」
:217 gradIn 为空指针 → 错误码 161001
:227 「dim 不等于 len(inputSizes)-1 且 dim 不等于 len(inputSizes)-2 轴。」→ 错误码 161002

源码根因

conversion/unfold_grad/op_api/aclnn_unfold_grad.cpp:

// :121  空指针统一返回内部错误码,而非 161001
CHECK_RET(CheckNotNull(gradOut, inputSizes, gradIn), ACLNN_ERR_INNER_NULLPTR);

// :128  只实现了「上界」约束,未实现文档要求的「双轴」约束
int64_t dimNum = gradOut->GetViewShape().GetDimNum();
CHECK_RET(dim >= 0 && dim < dimNum - 1, ACLNN_ERR_PARAM_INVALID);

两处问题:

  1. UG-02:ACLNN_ERR_INNER_NULLPTR(561103)属内部错误码段,而空指针是调用方的入参错误,文档明确承诺 161001。调用方按文档写的 if (ret == 161001) 分支永远不会命中,错误被归因到「算子内部异常」。

  2. UG-03::128 只判 dim >= 0 && dim < dimNum - 1,是单边界。文档要求的是「len-1 或 len-2 两个取值」,即 dim ∈ {dimNum-1, dimNum-2}。当 len(inputSizes)=3 时,dim=0 满足 0 <= 0 < 2,因此被放行;而文档 :227 明确这种情况应返回 161002。

环境信息

  • 硬件:Atlas A2 910B2(8 卡),npu-smi 26.1.1
  • OS / 架构:Linux aarch64
  • CANN:9.2.0
  • 编译器:g++ 11.4.0
  • 被检仓:cann/ops-math HEAD 3e45432d(2026-10-21)

重现步骤

1. 编译

g++ -std=c++17 -O2 unfold_grad_probe.cpp -o unfold_grad_probe \
    -I$ASCEND_HOME_PATH/include -L$ASCEND_HOME_PATH/lib64 \
    -lascendcl -lopapi -lnnopbase

2. 运行

./unfold_grad_probe

3. 用例构造

  • inputSizes(aclIntArray):{2, 3, 4},即 len=3,故合法 dim 只能是 2(len-1)或 1(len-2)
  • gradIn:fp32 [2,3,4](等于 inputSizes)
  • gradOut:按文档推导形状 (..., (sizedim-size)/step+1, size),size=2, step=1:
    • dim=2 → [2,3,3,2]
    • dim=1 → [2,2,4,2]
    • dim=0 → [2,3,4,2]
  • 每组用例只改一个变量,其余保持不变

4. 完整探针源码(可直接保存为 unfold_grad_probe.cpp)

// ============================================================================
// aclnnUnfoldGrad 实测:UG-02(空指针返回码)/ UG-03(dim 双轴约束未实现)
//
// 文档契约(conversion/unfold_grad/docs/aclnnUnfoldGrad.md):
//   :127  「dim 仅支持 len(inputSizes)-1 或者 len(inputSizes)-2」
//   :217  gradIn 为空指针 -> 161001
//   :227  dim 不等于 len(inputSizes)-1 且不等于 len(inputSizes)-2 -> 161002
//
// 源码现状(op_api/aclnn_unfold_grad.cpp):
//   :121  CHECK_RET(CheckNotNull(...), ACLNN_ERR_INNER_NULLPTR)   <- 返回 561103,非 161001
//   :128  CHECK_RET(dim >= 0 && dim < dimNum - 1, ...)            <- 只拦上界,未实现「双轴」约束
//
// 判据:
//   UG-02: 三个空指针(gradOut / inputSizes / gradIn)-> 实测 561103 即缺陷(文档要 161001)
//   UG-03: len(inputSizes)=3 时 dim=0 -> 文档要求 161002;实测 0 即缺陷
//          同时 dim=1(len-2) 与 dim=2(len-1) 应放行,作为对照
// ============================================================================
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdint>
#include <vector>

#include "acl/acl.h"
#include "aclnn/aclnn_base.h"
#include "aclnnop/aclnn_unfold_grad.h"

struct H { std::vector<int64_t> sh, st; aclTensor* t = nullptr; };

static H* mk(std::vector<int64_t> s, aclDataType d, void* p, aclFormat f = ACL_FORMAT_ND)
{
    auto* h = new H();
    h->sh = std::move(s);
    h->st.assign(h->sh.size(), 1);
    for (int i = static_cast<int>(h->sh.size()) - 2; i >= 0; --i) h->st[i] = h->st[i + 1] * h->sh[i + 1];
    h->t = aclCreateTensor(h->sh.data(), h->sh.size(), d, h->st.data(), 0, f, h->sh.data(), h->sh.size(), p);
    return h;
}

static void* M(size_t b)
{
    void* p = nullptr;
    if (b == 0) b = 256;
    aclrtMalloc(&p, b, ACL_MEM_MALLOC_HUGE_FIRST);
    return p;
}

static const char* rn(int rc)
{
    switch (rc) {
        case 0: return "SUCCESS";
        case 161001: return "161001 NULLPTR";
        case 161002: return "161002 PARAM";
        case 561002: return "561002 INNER";
        case 561103: return "561103 INNER_NULLPTR";
        case 361001: return "361001 RUNTIME";
        default: return "other";
    }
}

static int g_bad = 0;

// 构造一套合法 shape 并调用;传 nullptr 的实参用来测 UG-02
//   inputSizes = {2,3,4}(len=3),dim 由参数指定,size=2, step=1
//   gradOut 推导:shape (..., (sizedim-size)/step+1, size) 去掉附加维后
//     对 dim=2: gradOut = [2,3,(4-2)/1+1, 2] = [2,3,3,2]
//     对 dim=1: 同理 gradOut = [2,(3-2)/1+1,4,2] = [2,2,4,2]
//   gradIn = inputSizes = [2,3,4]
static void runCase(int64_t dim, bool nullGradOut, bool nullSizes, bool nullGradIn,
                    const char* tag, int expectRc)
{
    // gradIn 固定为 inputSizes 形状
    const size_t inElems = 24;
    void* dIn = M(inElems * sizeof(float));
    void* dOut = M(64 * sizeof(float));

    int64_t isBuf[3] = {2, 3, 4};
    aclIntArray* inputSizes = aclCreateIntArray(isBuf, 3);
    std::vector<int64_t> gradOutShape;
    if (dim == 2) gradOutShape = {2, 3, 3, 2};
    else if (dim == 1) gradOutShape = {2, 2, 4, 2};
    else gradOutShape = {2, 3, 4, 2};   // dim=0 时的「直觉」形状,仅用于让校验走到 dim 判断

    H* gradOut = mk(gradOutShape, ACL_FLOAT, dOut);
    H* gradIn = mk({2, 3, 4}, ACL_FLOAT, dIn);

    uint64_t ws = 0;
    aclOpExecutor* ex = nullptr;
    aclnnStatus st = aclnnUnfoldGradGetWorkspaceSize(
        nullGradOut ? nullptr : gradOut->t,
        nullSizes ? nullptr : inputSizes,
        dim, 2, 1,
        nullGradIn ? nullptr : gradIn->t,
        &ws, &ex);
    const int rc = static_cast<int>(st);

    const bool ok = (rc == expectRc);
    if (!ok) ++g_bad;
    printf("  %-46s dim=%-2ld rc=%-6d %-22s %s\n",
           tag, static_cast<long>(dim), rc, rn(rc),
           ok ? "符合预期" : (expectRc == 0 ? "!! 合法用例被拒" : "*** 不符合文档契约 ***"));
    if (ex != nullptr) aclDestroyAclOpExecutor(ex);
    aclDestroyIntArray(inputSizes);
}

int main()
{
    aclInit(nullptr);
    aclrtSetDevice(0);

    printf("========================================================================\n");
    printf(" aclnnUnfoldGrad: UG-02 空指针返回码 / UG-03 dim 双轴约束\n");
    printf(" 文档: :127「dim 仅支持 len(inputSizes)-1 或 -2」; :217 空指针->161001; :227 dim 越界->161002\n");
    printf(" 源码: :121 空指针->ACLNN_ERR_INNER_NULLPTR; :128 仅 dim>=0 && dim<dimNum-1\n");
    printf("========================================================================\n\n");

    printf("--- UG-03: len(inputSizes)=3,合法 dim 只能是 2(len-1) 或 1(len-2) ---\n");
    runCase(2, false, false, false, "对照: dim=2 (=len-1, 合法)", 0);
    runCase(1, false, false, false, "对照: dim=1 (=len-2, 合法)", 0);
    runCase(0, false, false, false, "dim=0 (既非 len-1 也非 len-2)", 161002);
    // 也测一下更极端的:dim 远小于 len-2
    runCase(0, false, false, false, "dim=0 重复确认稳定性", 161002);

    printf("\n--- UG-02: 三个必选参数分别传 nullptr(文档要求 161001)---\n");
    runCase(2, true, false, false, "gradOut = nullptr", 161001);
    runCase(2, false, true, false, "inputSizes = nullptr", 161001);
    runCase(2, false, false, true, "gradIn = nullptr", 161001);

    printf("\n========================================================================\n");
    printf(" 不符合契约的用例数: %d\n", g_bad);
    printf("========================================================================\n");

    aclrtResetDevice(0);
    aclFinalize();
    printf("\nDONE\n");
    return 0;
}

预期结果

用例 预期
dim=2(len-1) 0(合法对照)
dim=1(len-2) 0(合法对照)
dim=0(既非 len-1 也非 len-2) 161002
gradOut=nullptr 161001
inputSizes=nullptr 161001
gradIn=nullptr 161001

日志 / 截图

A2 910B2 / CANN 9.2.0 实跑:

--- UG-03: len(inputSizes)=3,合法 dim 只能是 2(len-1) 或 1(len-2) ---
  对照: dim=2 (=len-1, 合法)                 dim=2  rc=0      SUCCESS                符合预期
  对照: dim=1 (=len-2, 合法)                 dim=1  rc=0      SUCCESS                符合预期
  dim=0 (既非 len-1 也非 len-2)              dim=0  rc=0      SUCCESS                *** 不符合文档契约 ***
  dim=0 重复确认稳定性                    dim=0  rc=0      SUCCESS                *** 不符合文档契约 ***

--- UG-02: 三个必选参数分别传 nullptr(文档要求 161001)---
  gradOut = nullptr                              dim=2  rc=561103 561103 INNER_NULLPTR   *** 不符合文档契约 ***
  inputSizes = nullptr                           dim=2  rc=561103 561103 INNER_NULLPTR   *** 不符合文档契约 ***
  gradIn = nullptr                               dim=2  rc=561103 561103 INNER_NULLPTR   *** 不符合文档契约 ***

  不符合契约的用例数: 5

两个合法对照(dim=2、dim=1)均返回 0,证明 shape 推导与调用链正确,dim=0 的 rc=0 不是用例构造错误导致的误判。

备注

影响

  • UG-02:561103 属内部错误段。调用方按文档构造 161001 分支做「参数缺失」处理时会失效,错误被误判为算子内部故障,排查成本高。三个必选参数无一例外。
  • UG-03:dim 取值非法时被静默接受并执行。虽然实测 dim=0 的梯度结果与 float64 参考一致(见下),但契约失效本身意味着:若后续实现调整 dim 语义或优化路径,非法取值将不再有保护,且调用方无法在参数阶段发现自己的错误。

dim 越界被接受属「校验缺失」类问题,与结果正误是两个层面——本次未对 dim=0 的输出数值下结论,仅主张契约未实现。

查重说明

已检索 cann/ops-math 存量 unfold 相关 issue(10 条),确认非重复:

  • #3553(closed):「pad 系反向与 UnfoldGrad 空输入×非空出参早退不填输出,返回成功但出参为未初始化内存」——该单讲的是空张量早退路径不写输出(aclnn_unfold_grad.cpp:143-148),与本单的**空指针返回码(561103 vs 161001)**和 dim 值域约束是完全不同的两个检查点。
  • #2834(closed):「Host InferShape 未校验 inputSizes 长度导致栈缓冲区越界」——对象是 inputSizes 的长度校验,非空指针返回码,也非 dim 约束。
  • #941 / #944 / #1026 / #1820 / #3253 / #3255 / #3356 / #1036 / #942:分别为 uncontiguous 问题、部分场景不支持、格式处理、NZ 拦截、路由校验条件放宽、编译告警、aicpu 路径配置、950 适配,均与本单无关。
  • 未检索到「aclnnUnfoldGrad 空指针返回 561103 而非 161001」或「len(inputSizes)=3 时 dim=0 未被拒绝」的存量单。

建议修复

  1. UG-02:把空指针返回码改为文档承诺的 161001:
// op_api/aclnn_unfold_grad.cpp:121
CHECK_RET(CheckNotNull(gradOut, inputSizes, gradIn), ACLNN_ERR_PARAM_NULLPTR);
  1. UG-03:把单边界改为文档要求的双取值约束:
// op_api/aclnn_unfold_grad.cpp:128
int64_t dimNum = gradOut->GetViewShape().GetDimNum();
CHECK_RET(dim == dimNum - 1 || dim == dimNum - 2, ACLNN_ERR_PARAM_INVALID);
  1. 建议补两条 UT:三个必选参数各传 nullptr 断言 161001;len(inputSizes)=3 时 dim=0 断言 161002。当前 tests/ut/ 未见覆盖。
likedislike
liuhanwen8liuhanwen8
21 days ago Added label: bug-report
陈思
陈思Collaborators
20 days ago Comments:

您好,感谢反馈!已收到三个空指针用例及 inputSizes={2,3,4} 的 dim 对照结果。

问题分析

核对 cann/ops-math@3e45432d 的 CheckParams,空指针检查确实返回 ACLNN_ERR_INNER_NULLPTR,与文档的 161001 不一致;dim 校验允许所有满足 0 <= dim < rank(gradOut)-1 的轴,也与文档仅支持末两轴的表述不同。

这里需区分 rank(gradOut)=len(inputSizes)+1:末两轴应以 len(inputSizes) 为基准,不能直接套用正文建议中的 gradOut rank 表达式。另外该版本文档将 dim 约束列在 561002 下,并非 161002,返回码也需要与 #3797 一并梳理。

处理方向

可以考虑规范空指针返回码,并结合实际支持轴与兼容性确认 dim 的公开契约,再统一校验和文档。由 @chen_0715 接单继续确认;本条保留空指针和 dim 两项诉求。

likedislike
陈思
陈思Collaborators
20 days ago Comments:

/assign @chen_0715

likedislike
CANN-robotCANN-robotCollaborators
20 days ago Assign chen_0715 as responsible person
陈思陈思Collaborators
20 days ago Added label: pending-confirmation
CchenyifengCollaborators
20 days ago Assign tangkaidi as responsible person
CchenyifengCollaborators
20 days ago Removed responsible person chen_0715
陈思陈思Collaborators
20 days ago Removed label: pending-confirmation
TtangkaidiCollaborators
18 days ago Added label: confirmed
tangkaidiCollaborators
18 days ago Comments:

你好,感谢你的关注和反馈,该issue提到的问题已经确认。
修复PR链接:https://gitcode.com/cann/ops-math/pull/6650

  • 修复aclnn阶段如果入参为空指针时的返回错误码。
  • 修改资料内对允许传入的dim的范围。
    将会尽快合入修复代码。
likedislike
CANN-robotCANN-robotCollaborators
16 days ago Close issue
CANN-robotCANN-robotCollaborators
16 days ago Added label: resolved