Closed
[Bug-Report|缺陷反馈]: [Diag] aclnnDiag 在 diagonal=INT64_MIN(2D)或 INT64_MIN/INT64_MAX(1D)时进程 abort,应返回 161002 #3922
liuhanwen8Add on  16 days agoClosed at  8 days ago
liuhanwen8
liuhanwen8
16 days ago Create

现象

调用 aclnnDiagGetWorkspaceSize 时,diagonal=INT64_MIN 且输入为 2 维,进程以 SIGABRT 退出(rc=134,core dumped),没有返回任何错误码。同一份二进制在合法 diagonal 上返回 0。

组别 输入 观测
对照组 CTRL self=float32[3,4]、diagonal=1、out=[3] ret=0
缺陷组 DEFECT self=float32[3,4]、diagonal=-9223372036854775808、out=[0] 进程 abort,rc=134,无返回码

同族形态:self=float32[5](1 维)+ diagonal=INT64_MIN 或 INT64_MAX 同样 abort;diagonal=INT64_MIN+2、-INT64_MAX 返回 561103,不崩溃。

期望

资料《aclnnDiag》"约束说明"写明:输入为 2 维时 diagonal 应满足 -(m-1) <= diagonal <= (n-1)。越界属于非法入参,按 aclnn 错误码约定应返回 161002(ACLNN_ERR_PARAM_INVALID)。公开接口收到非法属性时应返回错误码,不应让调用进程崩溃。

环境

  • 硬件:Ascend 910B2(单卡)
  • CANN:9.2.0(Ascend-cann-toolkit 9.2.0 + ops 9.2.0)
  • 仓:cann/ops-math,master 最新提交 584b2ea6(复现前 git fetch 核对,-diagonal / rows + diagonal / std::abs(diagonal) 仍在)
  • 操作系统:Linux aarch64(glibc 2.35)

复现步骤

  1. 把下方代码保存为 repro_diag.cpp。
  2. 编译:g++ -std=c++17 -O2 repro_diag.cpp -o repro_diag -I$ASCEND_HOME_PATH/include -L$ASCEND_HOME_PATH/lib64 -lascendcl -lopapi -lnnopbase
  3. 跑对照:./repro_diag ctrl,打印 ret=0。
  4. 跑缺陷:./repro_diag defect,打印 diagonal=-9223372036854775808 -> 后进程 abort;echo $? 为 134。
  5. 验证 1 维形态:把程序里的 self 形状改为 {5}、out 形状改为 {0},diagonal 分别取 INT64_MIN 与 INT64_MAX,同样 abort。

复现脚本

// aclnnDiag: diagonal=INT64_MIN 时 GetWorkspaceSize 直接 abort(期望 161002)
// build: g++ -std=c++17 -O2 repro_diag.cpp -o repro_diag \
//          -I$ASCEND_HOME_PATH/include -L$ASCEND_HOME_PATH/lib64 -lascendcl -lopapi -lnnopbase
// run:   ./repro_diag ctrl ; ./repro_diag defect
#include <acl/acl.h>
#include <aclnnop/aclnn_diag.h>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <vector>

static aclTensor* mk(void* d, std::vector<int64_t> sh, aclDataType dt) {
    std::vector<int64_t> st(sh.size());
    int64_t acc = 1;
    for (int i = (int)sh.size() - 1; i >= 0; --i) { st[i] = acc; acc *= (sh[i] ? sh[i] : 1); }
    return aclCreateTensor(sh.data(), sh.size(), dt, st.data(), 0, ACL_FORMAT_ND,
                           sh.data(), sh.size(), d);
}

static void run(const char* tag, int64_t diag, std::vector<int64_t> outSh) {
    const int64_t n = 12;
    std::vector<int64_t> sh = {3, 4};
    std::vector<float> h(n);
    for (int64_t i = 0; i < n; ++i) h[i] = (float)(i + 1);
    int64_t on = 1;
    for (auto v : outSh) on *= v;
    void *dIn = nullptr, *dOut = nullptr;
    aclrtMalloc(&dIn, n * 4, ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMalloc(&dOut, (size_t)(on > 0 ? on * 4 : 8), ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMemcpy(dIn, n * 4, h.data(), n * 4, ACL_MEMCPY_HOST_TO_DEVICE);
    aclTensor* self = mk(dIn, sh, ACL_FLOAT);
    aclTensor* out = mk(dOut, outSh, ACL_FLOAT);
    uint64_t ws = 0;
    aclOpExecutor* ex = nullptr;
    printf("%s diagonal=%lld -> ", tag, (long long)diag);
    fflush(stdout);
    int r = (int)aclnnDiagGetWorkspaceSize(self, diag, out, &ws, &ex);
    printf("ret=%d\n", r);
    aclDestroyTensor(self);
    aclDestroyTensor(out);
    aclrtFree(dIn);
    aclrtFree(dOut);
}

int main(int argc, char** argv) {
    aclInit(nullptr);
    aclrtSetDevice(0);
    if (argc > 1 && std::strcmp(argv[1], "ctrl") == 0) {
        run("[对照组 CTRL]   diag=1(合法,out=[3])", 1, {3});
    } else {
        run("[缺陷组 DEFECT] diag=INT64_MIN(越界,out=[0])", INT64_MIN, {0});
    }
    aclFinalize();
    return 0;
}

影响

diagonal 常由外部参数/配置文件传入。任何批量取对角线、参数扫描或自动调优的调用方,一旦取到 INT64_MIN(或 1 维输入下的 INT64_MAX),调用进程直接退出而不是拿到错误码;在常驻服务里等价于一次拒绝服务。

建议

op_api/aclnn_diag.cpp 里三处有符号运算在极值下溢出,建议改为溢出安全写法并前置返回 161002:

  • 2 维分支:-diagonal < rows 与 rows + diagonal 用 __builtin_sub_overflow / __builtin_add_overflow 判定;diagonal == INT64_MIN 时直接判非法。
  • 1 维分支:std::abs(diagonal) 与 inputShape.GetShapeSize() + std::abs(diagonal) 同样做溢出检查。
  • 校验放在 InferDiagOutShape 之前,避免负维度下发到 l0op::DiagV2 的 tiling。
likedislike
liuhanwen8liuhanwen8
16 days ago Added label: bug-report
陈思
陈思Collaborators
15 days ago Comments:

您好,感谢反馈!按 cann/ops-math@584b2ea6cbaeba684a51a74296d82d4d50ab3b0f 核查,第一段的形状推导确实存在对 diagonal 极值不安全的有符号运算,不能只依赖后续内部错误返回来处理这些输入。

问题分析

InferDiagOutShape在一维输入时先计算 inputLength + std::abs(diagonal):INT64_MIN 的正绝对值无法用 int64 表示;对于长度 5,INT64_MAX + 5 也超出边长的表示范围。二维负偏移分支则先计算 -diagonal,同样无法安全处理 INT64_MIN。CheckDiagonal 的空边界判断还有同类取负操作。溢出后再检查输出 shape 已经太晚;非法长度可能继续进入形状处理,与您报告的进程异常相符,但具体 SIGABRT 触发点尚未独立复现确认。

当前第一段入口在类型、输入维数检查后就调用上述推导,没有先完成 diagonal 极值及输出尺寸可表示性检查。一维后续的 DiagFlat还会再次计算边长,因此上下层的安全条件也需保持一致。

处理方向

一个可行方案是在 InferDiagOutShape 之前完成受检算术与范围检查,非法参数明确返回 ACLNN_ERR_PARAM_INVALID(161002):一维先处理不可取绝对值的极值,再验证输入长度与偏移绝对值之和,以及输出元素总数能否表示;二维改用不需要对任意 diagonal 直接取负的安全比较,避免生成负长度。不能先执行有符号溢出,再通过结果是否为负来补救。

同时需保留或明确空输出边界的约定:源码专门处理了二维 diagonal==列数、diagonal==-行数 的空对角线情况,而资料约束比这一行为更窄。极值防御应先解决崩溃风险,不能未经兼容评估就顺带改变全部普通越界输入的行为;返回码说明也可同步明确值域和不可表示输出尺寸的限制。

回归可覆盖一维、二维的 INT64_MIN/INT64_MAX 及邻近值、普通合法偏移和现有空输出边界,分别检查第一段错误码与正常输出 shape。您已提供环境和最小对照,无需重复补交;我们会继续确认形状推导的安全检查与接口约定。

likedislike
陈思陈思Collaborators
15 days ago Assign chen_0715 as responsible person
陈思陈思Collaborators
15 days ago Added label: pending-confirmation
陈思陈思Collaborators
15 days ago Removed label: pending-confirmation
CchenyifengCollaborators
14 days ago Assign Anthony0331 as responsible person
CchenyifengCollaborators
14 days ago Removed responsible person chen_0715
Xu-XuePengXu-XuePengCollaborators
11 days ago Associated pull request
Xu-XuePengXu-XuePengCollaborators
10 days ago Deleted associated pull request: fix:issue 3922 3938 3940
Xu-XuePengXu-XuePengCollaborators
10 days ago Associated pull request
Xu-XuePengXu-XuePengCollaborators
10 days ago Deleted associated pull request: fix:issue 3922 3938 3940
Xu-XuePengXu-XuePengCollaborators
8 days ago Associated pull request
CANN-robotCANN-robotCollaborators
8 days ago Close issue
CANN-robotCANN-robotCollaborators
8 days ago Added label: resolved