Closed
[Bug-Report|缺陷反馈][工具检测]:topk 示例 isHasfinish=true 分支 `finishLocal.GetValue` 标量读与 DataCopy 的 MTE2 搬入之间缺少 MTE2_S 同步,racecheck 报 2 条 UB RAW竞争异常 #1736
h-glueAdd on Sep 18Closed at Sep 28
Sep 18 Assign lijunjie147 as responsible person
DianaBlessYou
Sep 22 Comments:
Sep 22 Comments:
您好,感谢反馈~
问题已定位,应该在finishLocal.GetValue之前补上与 PIPE_MTE2 写之间的 MTE2_S 事件同步。
相关pr会在近期合入,请关注。


DianaBlessYou
Sep 24 Comments:
Sep 24 Comments:
您好,相关PR已提,详情请参考:https://gitcode.com/cann/asc-devkit/pull/6352


DianaBlessYou
Sep 28 Comments:
Sep 28 Comments:
您好,相关问题已经合入,如果暂时没有其他问题,我们先关闭该 Issue,后续有任何疑问,欢迎重新提 Issue 讨论。


Sep 28 changed custom state from 待办的 to 已解决
Sep 28 Close issue
Sep 28 Added label: resolved
问题描述 (Mandatory / 必填)
在 asc-devkit 示例
examples/01_simd_cpp_api/04_advanced_api/05_sort/topk上启用 mssanitizer racecheck 检测,共报告 2 条 UB RAW hazard:PIPE_MTE2 WritevsPIPE_S ReadRAW()+0x180/RAW()+0x181(UB 上 finishLocal bool 数组相邻元素)关键特征:
AscendC::DataCopy(srcLocalFinish, srcGlobal3, finishLocalBytes / sizeof(bool))(CopyIn 中搬入 finish 标志),serialNo:156,目标为 UB0x180(32 字节),经 topk.asc:158 → topk.asc:309;bool finishValue = finishLocal.GetValue(j);,serialNo:377/463,读地址0x180/0x181——正是 UB bool 数组相邻两元素,与topKInfo.outter = 2的两轮循环迭代一一对应;调用栈kernel_tensor_impl.h:644(GetValue直接解引用__ubuf__)→topk_3510_impl.h:132→topk_3510_impl.h:1370→topk.h:119→ topk.asc:229(TopK 调用)→ topk.asc:159 → topk.asc:309;GetQueEvt决定,kernel_event.h:535-536 对src==GM && dst==UB只返回HardEvent::MTE2_V,全链路不产生MTE2_S事件;而LocalTensor::GetValue是__inout_pipe__(S)(kernel_tensor_impl.h:605),标量核直接在 PIPE_S 上读 UB,二者之间没有任何跨管道事件保护;GetValue(j)之后才执行SetFlag/WaitFlag<HardEvent::S_V>,该同步方向是 S→V(标量→向量),且发生在读之后,既无法保护本次 PIPE_S 读,也无法建立 MTE2→S 的顺序;topk_3510_impl.h的isHasfinish=true编译分支(topk_3510_impl.h:131-140);用户算子严格遵循 TQue/TQueBind/DataCopy/EnQue 标准范式,racecheck 在指令级模型下报告该 RAW 在硬件时序语义上是成立的(PIPE_MTE2 与 PIPE_S 确无事件同步);CompareResult golden_dstGmValue.bin success!、CompareResult golden_dstGmIndex.bin success!、test pass!——程序序上 MTE2 搬入先于 TopK 调用,实际执行正确;RAW 属"无显式同步但恰好有序"的可观测竞态,一旦编译器/硬件调度改变时序即存在读到未写完数据的风险。环境信息 (Mandatory / 必填)
examples/01_simd_cpp_api/04_advanced_api/05_sort/topkCMAKE_ASC_RUN_MODE=npu),CMAKE_ASC_ARCHITECTURES=dav-3510topk_custom,blockDim=1(单 AIV),模板参数isHasfinish=true(topk.asc:106),outter=2,3 个 VECIN 队列 + 2 个 VECOUT 队列finishLocal队列缓冲基址0x180,32 字节(bool × finishLocalBytes)重现步骤 (Mandatory / 必填)
cd examples/01_simd_cpp_api/04_advanced_api/05_sort/topkmkdir -p build && cd build export ASCFLAGS="-g --cce-enable-sanitizer" cmake .. -DCMAKE_ASC_ARCHITECTURES=dav-3510 -DCMAKE_ASC_LINK_FLAGS=--cce-enable-sanitizer make -jPotential RAW hazard detected at UB:写侧均为PIPE_MTE2 Write at RAW()+0x180(serialNo:156,topk.asc:171 DataCopy),读侧分别为PIPE_S Read at RAW()+0x180(serialNo:377)与PIPE_S Read at RAW()+0x181(serialNo:463),均指向库内 topk_3510_impl.h:132finishLocal.GetValue(j);CompareResult ... success!+test pass!,功能验证通过。预期结果 (Mandatory / 必填)
mssanitizer racecheck 对这两条 UB RAW hazard 不应误报为"用户算子缺少同步",但工具在指令级模型下报告该事件组合本身是准确的。预期如下:
TopK高阶 API(isHasfinish=true分支)应为内部的 PIPE_S 标量读提供正确的跨管道同步:在finishLocal.GetValue(j)(PIPE_S)之前补上与 PIPE_MTE2 写之间的MTE2_S事件同步(如WaitFlag<HardEvent::MTE2_S>),使 S 管道读严格发生在 MTE2 搬入之后;修复后 racecheck 将不再报告这两条 RAW hazard;CopyIn(topk.asc:164-176)→EnQue→TopK→DeQue是标准 TQue 队列范式,缺陷源头不在用户代码。相关日志/截图 (Mandatory / 必填)
完整日志文件:
result_fix/topk/san.log(2 条 RAW hazard)result_fix/topk/mssanitizer_20260917204213_2046249.log典型 RAW ERROR #1(写 MTE2 vs 读 PIPE_S,地址 +0x180):
典型 RAW ERROR #2(同一写,读地址 +0x181,serialNo:463):
指令流证据(mssanitizer 日志关键行):
地址映射与证据链分析(根因链):
0x12000001a000,32 字节(MALLOC serialNo:3)DataCopy(srcLocalFinish, ...),GM0x12000001a000→ UB0x180,32 字节,PIPE_MTE2finishLocal.GetValue(j),读 UB0x180/0x181(bool 数组相邻元素,outter=2两轮循环),PIPE_S,__inout_pipe__(S)直接解引用GetQueEvt(GM, UB, fwdDirect)只返回MTE2_V,无MTE2_S→ EnQue/DeQue 框架下全链无 MTE2→S 事件SetFlag/WaitFlag<HardEvent::S_V>在GetValue之后,S→V 方向,无法保护本次 S 读topk_3510_impl.hisHasfinish=true分支;用户算子为标准范式CompareResult ... success!+test pass!;程序序上 MTE2 搬入先于 TopK,功能正确,属"无同步但恰好有序"调用栈说明:
修复建议 (Optional / 选填)
方案 1(推荐,绕开受影响路径):用户侧将模板参数改为
isHasfinish = false(topk.asc:106),使编译进入不含finishLocal.GetValue的分支,可立即消除 2 条 UB RAW hazard。若业务确实需要 finish 标志(dstIndex用topKInfo.n填充未命中元素),则需权衡语义差异,不建议长期绕开。方案 2(CANN 库侧修复,根治):在 topk_3510_impl.h:131-140 的
isHasfinish分支中,为 PIPE_S 标量读补齐 MTE2→S 跨管道同步。参照GetQueEvt对 GM→UB 应返回MTE2_S的语义(HardEvent::MTE2_S为有效事件),在GetValue之前执行:if constexpr (isHasfinish) { auto eventID = GetTPipePtr()->FetchEventID(HardEvent::MTE2_S); WaitFlag<HardEvent::MTE2_S>(eventID); // 先保证 MTE2 搬入完成 bool finishValue = finishLocal.GetValue(j); // 再执行 PIPE_S 标量读 auto eventID2 = GetTPipePtr()->FetchEventID(HardEvent::S_V); SetFlag<HardEvent::S_V>(eventID2); // 原有 S→V 同步保留(供后续向量指令使用) WaitFlag<HardEvent::S_V>(eventID2); ... }(具体实现应以 CANN 内部同步接口规范为准,上例仅示意同步点位置与方向。)
方案 3(向 CANN 反馈):
TopK高阶 API 的isHasfinish=true分支存在"PIPE_S 标量读 UB 无 MTE2_S 事件保护"的库缺陷,建议提工单到 CANN 团队修复并补充 racecheck 回归用例。修复后验证:重新编译 + mssanitizer racecheck 运行,确认 2 条 UB RAW hazard 清零,且
CompareResult ... test pass!功能验证通过。同类风险排查:
TopKCompute中finishLocal.GetValue之外的标量访问(如GatherDstValAndDstIdx内的下标计算)以及其它高阶 API(Sort/ArgMax 等)中是否有类似的"MTE2 搬入 → PIPE_S 标量读无事件同步"模式,建议一并排查。影响评估:当前功能正确依赖"程序序上 MTE2 搬入必然先于 TopK 执行"这一事实,属可观测竞态。若编译器重排、流水深度变化或
finishLocal缓冲被复用,存在读到未写完数据的潜在风险,建议按方案 2 在库内根治。备注 (Optional / 选填)
./demo功能正确,CompareResult与test pass!通过;