已关闭
[Bug-Report|缺陷反馈]:aclDataBuffer 声明长度小于 OM 输入需求时GE读取到有效范围外数据 #510
nevermore创建于 8月11日关闭于 28 天前
yelongjian
8月11日 评论:
8月11日 评论:
感谢您的提问,针对您的问题,我们进行了初步分析:
针对仅支持INT64的OM,进行INT32的输入,会有输入未被拒绝并补齐非预期的值进行执行的问题;当前现象以模型为基准转换为INT64也属于合理现象;但是从设计层面,这个现象是否正确,我们仍需要深入分析,我们将尽快给您最新答复。


8月13日 将 wuzheng-hw 设为负责人
8月13日 移除了负责人 yelongjian
10 天前 添加了label:resolved
问题描述
在模型执行场景中观察到一个输入长度处理问题:模型输入在 OM 中需要 8 字节,但执行时传入的
aclDataBuffer.length只有 4 字节。GE 后续执行过程中仍按 OM 的DT_INT64[1]读取 8 字节,导致读取到aclDataBuffer.length之外的高 4 字节残留数据。本问题最终在
SequenceAt算子里表现为 index 异常大,并报input index is out of range。这里选择SequenceAt作为复现模型,是因为它会在日志中打印解析后的 index,便于确认 GE 实际读取到的输入值。从用户视角看,传入的低 4 字节 index 是0;异常值来自同一 device 地址释放后重分配留下的高 4 字节残留。复现中观测到的异常值如下:
32 位与 64 位口径说明
这里的“4 字节输入”指调用方本次实际提供的有效数据只有 4B,等价于低 32 位 index 值为
0。GE 后续 lowering 和算子执行使用 OM Data 节点描述;在复现日志中,CalcTensorSizeFromStorage的 data type 为9(INT64)。这里需要关注的是
aclDataBuffer.length=4B的有效边界语义。GE 已经拿到了该长度,后续仍按 OM 的DT_INT64[1]路径读取 8B,并把第 4 到第 7 字节的 device 残留数据当成本次输入的一部分。当前复现场景保持这个口径:OM 中index为INT64[1],运行时只写入并声明低 4B 有效数据。复现流程
构造了一个复现场景,走真实
ATC -> OM -> aclmdlLoadFromFile -> aclmdlExecute路径。本次提单暂不附全量复现代码,下面列出关键流程和关键代码片段;如需要可继续提供完整复现工程。1. 构造最小模型
ONNX 图只保留触发 index host 化和读取的最小链路:
其中
index在模型中声明为INT64[1],因此 OM 逻辑上需要至少 8 字节输入数据。X是正常输入,shape 为[2,1],拆分后 sequence 长度为 2,所以 index 取0时应是合法访问。2. 编译并加载 OM
复现路径使用 ATC 编译 ONNX,再通过 ACL 加载并执行 OM:
3. 构造 device 内存释放后同地址重分配场景
复现场景中先申请一块 device 内存并写入特征值,随后释放;之后重新申请 index 输入内存,并确认 driver/allocator 返回刚释放的同一 device 地址。
复现场景中按这个顺序构造:
uint64_t pattern[8] = {}; pattern[0] = 0x000012c000000000ULL; void *poison = nullptr; aclrtMalloc(&poison, 64, ACL_MEM_MALLOC_NORMAL_ONLY); aclrtMemcpy(poison, 64, pattern, 64, ACL_MEMCPY_HOST_TO_DEVICE); uintptr_t released = reinterpret_cast<uintptr_t>(poison); aclrtFree(poison);随后立即重新申请 64 字节 index buffer,并确认地址复用:
void *index_device = nullptr; aclrtMalloc(&index_device, 64, ACL_MEM_MALLOC_NORMAL_ONLY); if (reinterpret_cast<uintptr_t>(index_device) != released) { // 当前环境未复用刚释放的地址,停止本次复现,避免误判。 return SETUP_FAILED; }本地环境中第一次重申请即复用成功:
4. 本次 index 输入只写入并声明 4 字节
地址复用成功后,本次输入只写低 4 字节
index=0,不再写高 4 字节:uint32_t index = 0; aclrtMemcpy(index_device, sizeof(index), &index, sizeof(index), ACL_MEMCPY_HOST_TO_DEVICE);创建 dataset buffer 时,也只向 GE 声明 4 字节有效输入:
aclDataBuffer *buffer = aclCreateDataBuffer(index_device, 4); aclmdlAddDatasetBuffer(input_dataset, buffer);复现中还给该输入设置了用户侧 tensor desc:
int64_t dims[] = {1}; aclTensorDesc *desc = aclCreateTensorDesc(ACL_FLOAT, 1, dims, ACL_FORMAT_ND); aclmdlSetDatasetTensorDesc(input_dataset, desc, index_input_index);此时输入状态如下:
5. 执行模型
执行
aclmdlExecute后,GE 日志中可以看到下游算子读取到的 index 是20615843020800,与释放前残留拼接后的 8 字节值一致。实际结果
runner 关键输出:
关键日志片段:
对照验证
为确认异常 index 来自
aclDataBuffer.length=4B之外的高 4 字节残留,增加了一个对照场景。对照场景保持模型、输入声明、device 地址复用、本次只写低 4 字节index=0等条件不变,只在释放污染块前把同一块 device 内存刷 0。对照场景关键差异:
uint64_t zero[8] = {}; aclrtMemcpy(poison, 64, zero, 64, ACL_MEMCPY_HOST_TO_DEVICE); aclrtFree(poison);对照场景 runner 关键输出:
关键日志片段:
同一个日志文件中未出现
input index is out of range或input index 20615843020800 is not valid。两组结果对比:
该对照结果说明当前执行路径会受到声明长度外高 4 字节内容影响。建议 GE 仍按
aclDataBuffer.length对输入有效长度做边界校验,避免释放后残留数据参与本次模型计算。期望行为
请 GE 在模型执行入口、host 化拷贝前,或算子读取输入前增加输入有效长度校验。
对于本例:
GE 不应把
aclDataBuffer.length之外的 device 内存内容作为本次模型输入参与计算。初步定位线索
从现象看,下面几个位置可能需要补充或确认长度校验逻辑。
api/acl/acl_model/model/model_om2.cppPrepareOm2Tensor会把aclDataBuffer的地址和长度写入gert::TensorData:tensor[i].MutableTensorData().SetAddr(dataBuffer->data, nullptr); tensor[i].MutableTensorData().SetSize(dataBuffer->length); tensor[i].SetDataType(tensorDesc[i].GetDataType());这里
TensorData::GetSize()已经记录了 4B,后续流程需要继续使用这个有效长度约束数据读取。runtime/v2/kernel/common_kernel_impl/memory_copy.cchost 化拷贝路径里如果按 OM dtype/shape 推导出
tensor_size,建议在拷贝前确认tensor_size <= tensor_data->GetSize():rtMemcpyEx(host_block->GetAddr(), tensor_size, tensor_data->GetAddr(), tensor_size, RT_MEMCPY_DEVICE_TO_HOST);runtime/v2/engine/aicpu/converter/sequence/sequence_at_op.cc算子侧根据
DT_INT64解引用 index 前,也建议确认输入数据长度至少满足sizeof(int64_t):case ge::DT_INT64: index = *(static_cast<int64_t *>(index_data->GetAddr())); break;