Closed
[Bug-Report|缺陷反馈]: aclnnAddN 空 tensorList(Size()==0)时 (*tensors)[0] 先于空列表早退分支被解引用,宿主 SIGSEGV #3558
liuhanwen8Add on 28 days agoClosed at 11 days ago
28 days ago Added label: bug-report
陈思
27 days ago Comments:
27 days ago Comments:
您好,感谢反馈!
问题分析
核查 cann/ops-math master 8f1aae7e1927 的 AddN 实现,CheckNotNull 对非空的空列表对象会通过,随后 CheckParams 就在 dtype 比较中读取 (*tensors)[0],而入口的 Size()==0 处理位于 CheckParams 之后;形状与格式检查也依赖首元素。这个顺序确实存在空列表越界访问,能解释您提供的宿主 SIGSEGV 现象。
处理方向
可将不依赖列表元素的检查与非空列表检查拆开:先保留列表/out 指针等基础检查,再处理空列表,只有确认长度非零后才读取首元素。不能把 Size() 判断直接移到列表指针校验之前;CheckParams 返回的是 aclnnStatus,也不能用 return true 作为成功码。
当前文档明确写了“支持空 TensorList,此时输出为空 Tensor”,但 out 一栏又写“不支持空 Tensor”,两处说明存在冲突。修复时需要同步统一这一契约,避免只是消除崩溃,却对非空 out 无条件成功返回而不写输出。可用空列表、空指针、单元素正常列表以及空/非空 out 组合确认返回行为。我们会继续跟踪。


12 days ago Associated pull request
12 days ago Deleted associated pull request: fix(add_n): 补齐输入dtype一致性校验并修复空TensorList段错误(issue #3652)
12 days ago Deleted associated pull request: fix(add_n): 补齐输入dtype一致性校验并修复空TensorList段错误(issue #3652)
12 days ago Associated pull request
11 days ago Close issue
11 days ago Added label: resolved
11 days ago Deleted associated pull request: fix(add_n): 补齐输入dtype一致性校验并修复空TensorList段错误(issue #3652)
11 days ago Deleted associated pull request: fix(add_n): 补齐输入dtype一致性校验并修复空TensorList段错误(issue #3652)
Describe the current behavior / 问题描述 (Mandatory / 必填)
aclnnAddNGetWorkspaceSize传入空 tensorList(指针非空、Size()==0)时,在CheckParams内就通过(*tensors)[0]访问了空容器,早于入口的空列表早退分支,宿主侧空/越界解引用导致进程崩溃(SIGSEGV)。math/add_n/op_api/aclnn_add_n.cpp(当前 master):// CheckParams(入口 L133 调用) 103: static aclnnStatus CheckParams(const aclTensorList* tensors, const aclTensor* out) ... 108: OP_CHECK_DTYPE_NOT_SAME((*tensors)[0], out, return false); // 首次 [0] 越界 117: CHECK_RET(CheckShape(tensors, out), ACLNN_ERR_PARAM_INVALID); 119: if (IsPrivateFormat((*tensors)[0]->GetStorageFormat())) { // 第三处 [0] // CheckShape 内 85: auto shape = (*tensors)[0]->GetViewShape(); // 第二处 [0] // 入口:空列表早退在 CheckParams 之后,被架空 133: auto ret = CheckParams(tensors, out); 134: CHECK_RET(ret == ACLNN_SUCCESS, ret); 136: if (tensors->Size() == 0) { // 设计上的空列表处理,但已不可达 137: *workspaceSize = 0; 138: uniqueExecutor.ReleaseTo(executor); 139: return ACLNN_SUCCESS; 140: }要点:
CheckNotNull(L45OP_CHECK_NULL(tensors))对"指针非空但Size()==0"判空通过,逐元素循环 0 次亦通过;aclTensorList的operator[]无边界检查,空容器[0]为越界访问;CheckParams之后,对 L108/L85/L119 的访问不可达 —— 属"先解引用、后判断"。Environment Information / 环境信息 (Mandatory / 必填)
Steps to reproduce the issue / 重现步骤 (Mandatory / 必填)
构造空 tensorList 后直调第一段接口(
aclCreateTensorList(arr, 0)返回的列表指针非空且 Size()==0):#!/usr/bin/env python3 import ctypes as C, sys, os 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 cl.aclInit(None); cl.aclrtSetDevice(int(os.environ.get("DEV","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,0,ss,0,2,dd,n,p) # FLOAT/ND G=op.aclnnAddNGetWorkspaceSize; G.restype=C.c_int G.argtypes=[P,P,C.POINTER(C.c_uint64),C.POINTER(P)] case = sys.argv[1] if len(sys.argv)>1 else "empty" tl = op.aclCreateTensorList((P*1)(), 0) if case=="empty" else op.aclCreateTensorList((P*1)(mk([4])), 1) print("tensorList =", tl, flush=True) ws=C.c_uint64(0); ex=P() r=G(tl, mk([4]), C.byref(ws), C.byref(ex)) print("ret =", r, flush=True)运行:
source <CANN>/set_env.sh && python3 repro.py empty/python3 repro.py controlDescribe the expected behavior / 预期结果 (Mandatory / 必填)
空 tensorList 应走到设计好的早退分支(
*workspaceSize = 0+ 返回ACLNN_SUCCESS,空图);或按契约返回明确错误码。不应发生进程崩溃。Related log / screenshot / 日志 / 截图 (Mandatory / 必填)
实测(Atlas 800T A2 / CANN 9.2.0-beta.2,每例独立进程):
rc=0SUCCESS(ws=0)核心转储已确认(
code=11,SIGSEGV)。Special notes for this issue/备注 (Optional / 选填)
aclnnAddN相关命中均为 FusedMulAddN/文档/ST 用例,与本缺陷不同)。建议关联 #3554(aclnnCat / aclnnChunkCat 空 TensorList 零退分支返回 ACLNN_SUCCESS 未写 *workspaceSize)——同属"空 tensorList / 零退分支"区域但缺陷不同(#3554 为出参契约漏写,本单为[0]越界先于早退),非重复。CheckParams之前;或在CheckParamsL108、CheckShapeL85、L119 之前加if (tensors->Size() == 0) { return true; }短路。