| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
engram support training Co-authored-by: luozhonglin222<luozhonglin1@huawei.com> # message auto-generated for no-merge-commit merge: !8914 merge master into master engram support training Created-by: luozhonglin222 Commit-by: luozhonglin222 Merged-by: cann-robot Description: ## 描述 engram算子支持训练HOST代码,前向复用现有接口,增加相应参数,反向增加新接口 ## 关联的Issue https://gitcode.com/cann/ops-transformer/issues/3838 ## 测试 本地验证 ## 文档更新 不涉及 ## 类型标签 <!-- [x] 表示选中 --> - [ ] 🐛 Bug修复 - [x] ✨ 新特性 - [ ] ⚡ 性能优化 - [ ] ♻️ 重构 - [ ] 🧪 测试 - [ ] 📦 构建/CI - [ ] 🔧 配置变更 - [ ] 📝 文档更新 - [ ] ⬆️ 依赖升级 - [ ] 🔒 安全修复 - [ ] 🧹 代码清理 - [ ] ❓ 其他,请描述: # PR #8914 代码检视报告 ## 检视概览 - **PR URL**:https://gitcode.com/cann/ops-transformer/pull/8914 - **算子名**:engram_fetch_grad / engram_fetch_train / elastic_buffer - **代码侧别**:Kernel + Tiling + 混合(torch_extension) - **变更文件数**:21 - **文件组数**:6(K1, K2, H1, H2, S, E) - **总条例数**:346 - **检视时间**:2026-07-21 - **检视模式**:大型 PR 检视(21 文件 > 10 阈值) ## 检视统计 | 状态 | 条例数 | 占比 | |------|--------|------| | PASS | 341 | 98.6% | | FAIL(发现问题) | 5 | 1.4% | | SUSPICIOUS(需关注) | 0 | 0% | ### 逐文件组统计 | 文件组 | 侧别 | 文件数 | PASS | FAIL | |--------|------|--------|------|------| | engram_fetch_grad_kernel_G1 (K1) | Kernel | 3 | 27 | 0 | | engram_fetch_train_kernel_G1 (K2) | Kernel | 3 | 37 | 0 | | engram_fetch_grad_host_G1 (H1) | Tiling | 3 | 66 | 2 | | engram_fetch_train_host_G1 (H2) | Tiling | 3 | 70 | 2 | | shared (S) | 混合 | 6 | 61 | 1 | | elastic_buffer (E) | 混合 | 3 | 80 | 0 | | **合计** | — | **21** | **341** | **5** | ### 系统性风险 **SEC-4.1 / TOPK-7 在 H1 和 H2 中均失败**:两个 Tiling 侧文件组(engram_fetch_grad 和 engram_fetch_train)均存在 sendCounts/recvLocalEntry(或 sendCountsOut/recvLocalEntryOut)的 dim(0) 值未做非负/范围校验的问题。两个算子的 tiling 代码结构高度相似,校验缺失模式相同,建议全局排查并统一修复。 --- ## 发现问题(HIGH 置信度) ### 1. [engram_fetch_grad_host_G1] SEC-4.1 外部输入数据需要做合法性校验 - **问题描述**:sendCounts 的 dim(0) 和 recvLocalEntry 的 dim(0) 作为外部输入(来自 graph shape)仅校验了维度数(1D),未对 dim(0) 的值做合法性校验,违反 SEC-4.1「外部输入数据需要做合法性校验且确保校验范围正确」。同一函数中 commContext(行 206 GetDim(0) <= 0)和 gradFetched(行 221 numTokens < 0、行 226 hiddenDim <= 0)均对 dim 值做了显式校验,sendCounts/recvLocalEntry 缺失同类校验,形成不一致的安全缺口。 **风险链路 1(sendCounts)**:dim(0) 若为负值或超过 UINT32_MAX,行 260 static_cast<uint32_t> 会发生窄化截断/回绕,rankSize 变为巨大错误值 → SetBlockDim 和 workspace 计算均受影响。 **风险链路 2(recvLocalEntry)**:dim(0) 赋值给 int64_t totalRecv(行 281),若为负值则保留负数 → wsRecvGrad = totalRecv * hiddenBytes 为负 → wsTotal 求和可能为负 → static_cast<size_t>(wsTotal) 将负数回绕为极大 size_t 值,导致 workspace 申请异常。 - **代码片段**(mc2/engram_fetch_grad/op_host/op_tiling/engram_fetch_grad_tiling.cpp 行 251-281): cpp // sendCounts: 1D (W,) const gert::StorageShape *sendCountsShape = context->GetInputShape(IN_SEND_COUNTS); OP_TILING_CHECK(sendCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCounts", (std::to_string(sendCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCounts must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsShape->GetStorageShape().GetDim(0)); // recvCounts: 1D (W,) const gert::StorageShape *recvCountsShape = context->GetInputShape(IN_RECV_COUNTS); OP_TILING_CHECK(recvCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvCounts", (std::to_string(recvCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvCounts must be 1D."), return ge::GRAPH_FAILED); // recvLocalEntry: 1D (R,) const gert::StorageShape *recvLocalEntryShape = context->GetInputShape(IN_RECV_LOCAL_ENTRY); OP_TILING_CHECK(recvLocalEntryShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvLocalEntry"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvLocalEntryShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvLocalEntry", (std::to_string(recvLocalEntryShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvLocalEntry must be 1D."), return ge::GRAPH_FAILED); totalRecv = recvLocalEntryShape->GetStorageShape().GetDim(0); - **假设检验证据**: - (+40%) 规范违反:SEC-4.1 要求外部输入数据需做合法性校验,sendCounts dim(0) 直接 cast uint32_t、recvLocalEntry dim(0) 直接赋值 int64_t,均无值范围校验 - (+30%) 上下文防御缺失:同函数内 commContext/gradFetched 均有非负校验,唯独 sendCounts/recvLocalEntry 缺失 - (+20%) PR 归属:新增文件,全部行在 diff 变更范围内 - (+15%) 数据流风险:rankSize 负值回绕 / totalRecv 负值导致 workspace 回绕为巨大值 - 自信值 = 105% ≥ 70% → 判定违规 - **修复建议**: 1. 在行 260 之前增加 sendCounts dim(0) 值校验(> 0) 2. 在行 281 之前增加 recvLocalEntry dim(0) 值校验(>= 0) 3. 参照同函数内 commContext/gradFetched 的校验模式 --- ### 2. [engram_fetch_grad_host_G1] TOPK-7 融合规则/InferShape/Tiling 外部输入校验 - **问题描述**:TOPK-7 要求「Tiling 外部输入使用时必须进行合法性校验」,校验内容包括「Shape 维度是否合法(不为负数、不超过限制)」。sendCounts dim(0) 和 recvLocalEntry dim(0) 作为外部输入被提取后直接使用,未做非负/范围校验。同函数内 commContext dim0(<=0 校验)、gradFetched dim0(<0 校验)、hiddenDim(<=0 校验)均有非负防御,唯独 sendCounts 和 recvLocalEntry 缺失。此问题与 SEC-4.1 同构。 - **代码片段**(mc2/engram_fetch_grad/op_host/op_tiling/engram_fetch_grad_tiling.cpp 行 251-281): cpp // sendCounts: 1D (W,) const gert::StorageShape *sendCountsShape = context->GetInputShape(IN_SEND_COUNTS); OP_TILING_CHECK(sendCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCounts", (std::to_string(sendCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCounts must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsShape->GetStorageShape().GetDim(0)); // ...(recvLocalEntry 同理) totalRecv = recvLocalEntryShape->GetStorageShape().GetDim(0); - **假设检验证据**: - (+40%) 规范违反:TOPK-7 要求 Shape 维度不为负数 - (+30%) 上下文防御缺失:同函数内其他 tensor 均有非负校验 - (+20%) PR 归属:新增文件 - (+15%) 数据流风险:负值 dim0 经 static_cast 回绕或以负 int64_t 传播至 workspace 计算 - 自信值 = 105% ≥ 70% → 判定违规 - **修复建议**:与 SEC-4.1 同一问题,修复方案一致。 --- ### 3. [engram_fetch_train_host_G1] SEC-4.1 外部输入数据需要做合法性校验 - **问题描述**:CheckTensorDim 中 sendCountsOut 和 recvLocalEntryOut 的 dim0 值作为外部输入未做范围校验。rankSize(uint32_t)通过 static_cast 从 int64_t 缩窄转换,若 dim0 为负或超过 UINT32_MAX 将静默截断/回绕为巨大值;totalRecv(int64_t)未校验非负,若为负值将导致 wsLocalData = totalRecv * hiddenBytes 为负,进而 wsTotal 为负,再经 static_cast<size_t>(wsTotal) 回绕为约 1.8e19 的巨大 workspace 申请。同函数内 commContextShape dim0 > 0(行 188)、numTokens >= 0(行 202)、numRecvOutShape dim0 == 1(行 272)均已校验,此处校验明显不一致。 - **代码片段**(mc2/engram_fetch_train/op_host/op_tiling/engram_fetch_train_tiling.cpp 行 235-262): cpp const gert::StorageShape *sendCountsOutShape = context->GetOutputShape(OUT_SEND_COUNTS); OP_TILING_CHECK(sendCountsOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCountsOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCountsOut", (std::to_string(sendCountsOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCountsOut must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsOutShape->GetStorageShape().GetDim(0)); const gert::StorageShape *recvCountsOutShape = context->GetOutputShape(OUT_RECV_COUNTS); OP_TILING_CHECK(recvCountsOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvCountsOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvCountsOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvCountsOut", (std::to_string(recvCountsOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvCountsOut must be 1D."), return ge::GRAPH_FAILED); const gert::StorageShape *recvLocalEntryOutShape = context->GetOutputShape(OUT_RECV_LOCAL_ENTRY); OP_TILING_CHECK(recvLocalEntryOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvLocalEntryOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvLocalEntryOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvLocalEntryOut", (std::to_string(recvLocalEntryOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvLocalEntryOut must be 1D."), return ge::GRAPH_FAILED); totalRecv = recvLocalEntryOutShape->GetStorageShape().GetDim(0); - **假设检验证据**: - (+40%) 规范违反:外部 shape dim 值未做范围/非负校验 - (+30%) 上下文防御缺失:作用域内仅校验 dim 数,无 dim 值范围校验 - (+20%) PR 归属:新文件,全量代码均在 diff 范围 - (+15%) 数据流风险:rankSize 缩窄截断 / totalRecv 负值 → workspace 回绕 - 自信值 = 105% ≥ 70% → 判定违规 - **修复建议**: 1. 在行 243 前增加 sendCountsOut dim(0) 非负及 <= UINT32_MAX 校验 2. 在行 262 前增加 recvLocalEntryOut dim(0) >= 0 校验 3. 参照同函数内 numTokens(行 202-205)的校验模式 --- ### 4. [engram_fetch_train_host_G1] TOPK-7 融合规则/InferShape/Tiling 外部输入校验 - **问题描述**:与 SEC-4.1 同一问题。TOPK-7 要求 Tiling 外部输入的 Shape 维度不为负数、不超过限制。sendCountsOut dim0(赋值给 rankSize,缩窄为 uint32_t)和 recvLocalEntryOut dim0(赋值给 totalRecv)均未做非负/范围校验。同函数内 commContextShape dim0 > 0(行 188)、indicesShape numTokens >= 0(行 202)均已校验,存在校验不一致。 - **代码片段**(mc2/engram_fetch_train/op_host/op_tiling/engram_fetch_train_tiling.cpp 行 235-262):(同 SEC-4.1) - **假设检验证据**: - (+40%) 规范违反:Tiling 外部 shape dim 值未做非负/范围校验 - (+30%) 上下文防御缺失:仅校验 dim 数无 dim 值校验 - (+20%) PR 归属:新文件全量在 diff 范围 - (+15%) 数据流风险:rankSize 缩窄截断 / totalRecv 负值 → work See merge request: cann/ops-transformer!8914 | 1 个月前 | |
engram反向超时DFX Co-authored-by: luozhonglin222<luozhonglin1@huawei.com> # message auto-generated for no-merge-commit merge: !11577 merge master into master engram反向超时DFX Created-by: luozhonglin222 Commit-by: luozhonglin222 Merged-by: cann-robot Description: ## 描述 1.反向超时的DFX 2.UB溢出修复 3.scatter汇总和sort分核一致性修复 ## 关联的Issue <!-- 如果这个PR是为了解决特定的Issue,请在这里提供Issue链接。例如:关联Issue #000--> <!-- 如果这个PR是为了解决特定的问题单,请在这里描述问题单单号。--> ## 测试 <!--描述进行了哪些测试来验证你的改动。包括但不限于二级冒烟、算子泛化等。--> ## 文档更新 <!--如果这个PR包含文档的更新,请在这里指出。例如:更新了README.md文件。--> ## 类型标签 <!-- [x] 表示选中 --> - [x] 🐛 Bug修复 - [ ] ✨ 新特性 - [ ] ⚡ 性能优化 - [ ] ♻️ 重构 - [ ] 🧪 测试 - [ ] 📦 构建/CI - [ ] 🔧 配置变更 - [ ] 📝 文档更新 - [ ] ⬆️ 依赖升级 - [ ] 🔒 安全修复 - [ ] 🧹 代码清理 - [ ] ❓ 其他,请描述: PR #11577 代码检视报告 ## 检视概览 - **PR**: https://gitcode.com/cann/ops-transformer/pull/11577 - **算子**: engram_fetch_grad(mc2/engram_fetch_grad/) - **代码侧别**: 混合(Tiling 1 文件 + Kernel 4 文件) - **变更规模**: 5 个文件,有效变更约 207 行(+120 / -87) - **变更主题**: ① Host 侧新增 sort/unique 两阶段 UB 池峰值 fail-fast 校验(CalcSortUbBytes 镜像 Kernel GetUbSize),Kernel 侧 unique 阶段 entryBuf_ 由 64KB 收缩为 uniqueEntryBytes_;② 删除 RUNTIME_ABORT 宏与 COMM_RETRY_COUNT 重试,8 处失败点统一 ascendc_assert 直报,TimeoutCheck 增加 TimeoutSite;③ sort ProcessHist/ProcessScatter tile→core 归属由 batch-major 改 core-major;④ numRecvCores_ 扣除 flagCore 修正 - **检视文档**: cpp-secure / cpp-general / ascendc-api / ascendc-perf / ascendc-topk / mc2-specific / regbase-review-checks / simt-api-analysis(compile-secure / python-secure / cpp-style 按声明式匹配跳过) - **总条例数**: 135 条(32 组 / 6 波并行检视) - **检视时间**: 2026-09-09 - **配套产物**: code_summary.md(代码概要+变量溯源)、api_prestudy.md(API 约束预研) ## 检视统计 | 状态 | 条例数 | 占比 | |------|------|------| | PASS | 123 | 91.1% | | FAIL(发现问题)| 6 | 4.4% | | SUSPICIOUS(需关注/疑似)| 6 | 4.4% | | 范围外备注 | 1 | - | ## 核心结论 **6 个 FAIL 高度同源,可归并为 2 个根因:** - **根因 A(4 个 FAIL:cpp-secure 1.2/1.3/4.1、cpp-general 1.1/12.1)**:本 PR 删除 RUNTIME_ABORT 宏(含 while(true) release 兜底)并将 8 处失败点改为 ascendc_assert。经 CANN 9.0.0 头文件实证(kernel_operator_dump_tensor_intf.h:65-68),未定义 ASCENDC_DUMP 时 ascendc_assert 展开为**空宏**,且本算子构建未定义 ASCENDC_DUMP。release 产物下:commContext(设备侧外部数据)rankSize 一致性校验完全失效 → 除零 UB / workspace displs 越界写;pendingHandles 固定数组(8 元素)越界守卫失效;通信失败/超时静默继续(且删除了原有 3 次重试)。 - **根因 B(1 个 FAIL:cpp-secure 2.2)**:numRecvCores_==0 兜底分支中 totalBlocks_==1 时 numSendCores_ 由 0 改为 1,导致 recvIdx = aivId_ - numSendCores_ 在 aivId_=0 时发生 uint32 回绕,接收循环零迭代,单核形态多 rank 场景远端梯度静默丢失(功能回归)。 **正面确认**:sort tile 归属 core-major 改写修复了 tileOffsets 跨核免屏障假设失效的竞态(多方交叉验证一致);UB 池收缩(entryBuf_ 64KB→uniqueEntryBytes_)Host/Kernel 两侧公式逐项核算等值、构造性安全;新增两道 Host fail-fast 校验(sortUb 峰值 / minUniqueNeed)release 生效,覆盖 Host 可建模部分。 --- ## 发现问题(HIGH 置信度) ### [cpp-secure 2.2] 无符号回绕 —— recvIdx 回绕致单核形态接收零迭代 - **问题描述**:PR 修改 Init() 的单核 fallback(numSendCores_ 旧值 totalBlocks_-1U(totalBlocks_==1 时为 0)改为 1)后,RecvGradFromPeers 中无守卫的 uint32 减法 aivId_ - numSendCores_ 在 totalBlocks_==1 && numRanks_>1 时发生 0-1=0xFFFFFFFF 回绕。totalBlocks_==1 时 isReceiver_ 恒为 true(:525),回绕后的 recvIdx 使 for (wIdx = recvIdx; wIdx < totalWorkUnits; ...) 零迭代——本核静默跳过全部接收,对端发送核在 credit/tokenRead 计数器上自旋直至超时。旧代码该形态 numSendCores_=0、recvIdx=0 可完整覆盖(功能回归);Host 侧无 aivNum 与 rankSize 的关联校验,PR 又为 totalBlocks_<=1 专门设置了同核收发 fallback 分支,说明该形态是被明确支持的目标场景。 - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_grad_arch35.h 行 514-526): cpp numSendCores_ = sendersPerRank_ * numRanks_; if (numSendCores_ > halfBlocks) { numSendCores_ = halfBlocks; } numRecvCores_ = (totalBlocks_ > numSendCores_ + 1U) ? (totalBlocks_ - numSendCores_ - 1U) : 0U; if (numRecvCores_ == 0U) { numRecvCores_ = 1U; numSendCores_ = (totalBlocks_ > 1U) ? (totalBlocks_ - 1U) : 1U; // ← 本 PR 新增:totalBlocks_==1 时 numSendCores_=1 } isSender_ = (aivId_ < numSendCores_) || (totalBlocks_ <= 1U); isReceiver_ = (aivId_ >= numSendCores_ && aivId_ < totalBlocks_ - 1U) || (totalBlocks_ <= 1U); // totalBlocks_==1 时恒 true isFlagCore_ = (aivId_ == totalBlocks_ - 1U); - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_grad_arch35.h 行 968-988): ``cpp __aicore__ inline void EngramFetchGradArch35::RecvGradFromPeers() { if (!isReceiver_) { return; } GM_ADDR localWinBase = (GM_ADDR)ctxPtr_->commBuffer[rankId_]; uint32_t recvIdx = aivId_ - numSendCores_; // ← aivId_=0, numSendCores_=1 时回绕为 0xFFFFFFFF uint32_t totalWorkUnits = (numRanks_ - 1U) * sendersPerRank_; if (totalWorkUnits == 0U) { return; } // Each work unit must be owned by EXACTLY one receiver core: with numRecvCores_ > // totalWorkUnits, recvIdx % totalWorkUnits would map several cores onto the same // (srcRank, senderIdx) unit and race on the tokenRead counters (flow-control slots // get reused while the slower duplicate is still copying -> recvGrad corruption). for (uint32_t wIdx = recvIdx; wIdx < totalWorkUnits; wIdx += numRecvCores_) { // ← 0xFFFFFFFF < totalWorkUnits 恒假,零迭代 uint32_t adjustedSrcRank = wIdx / sendersPerRank_; ` - **假设检验证据**: - 正向证据: | 证据类型 | 分值 | 证据描述 | |---------|------|---------| | 规范违反 | +40% | uint32 减法无守卫,回绕实证(回绕值用作循环起始索引,命中条例"减法回绕"模式)| | 上下文防御缺失 | +30% | aivId_ < numSendCores_ 场景无钳位/分支防护 | | PR 归属 | +20% | 回绕可达性由本 PR :522 fallback 0→1 直接引入 | | 数据流风险 | +15% | numSendCores_ 值域被 PR 改变 | | 领域关联 | +10% | 领域规则交叉印证(TIL-1 独立检出同一问题)| - 负向证据: | 证据类型 | 分值 | 证据描述 | |---------|------|---------| | 防御存在 | -20% | 循环条件 wIdx < totalWorkUnits 兜住内存访问,后果限于功能失效而非越界 | - 自信值 = 40+30+20+15+10-20 = **95% ≥ 70% → 判定违规** - **修复建议**:fallback 分支保持 totalBlocks_==1 时 numSendCores_=0(维持旧覆盖行为),或将接收循环起点改为饱和计算 uint32_t recvIdx = (aivId_ > numSendCores_) ? (aivId_ - numSendCores_) : 0U;;同时建议在 fallback 中保证至少有一个核的 recvIdx 落在 [0, totalWorkUnits) 内。 ### [cpp-secure 4.1] 外部输入校验 —— rankSize 一致性校验 release 归零 - **问题描述**:PR 将 commContext(设备侧外部数据)rankSize 一致性校验的强制力由"release 下降级为确定性挂死(RUNTIME_ABORT 的 while(true) 兜底)"改为纯 ascendc_assert。已验证本算子构建不定义 ASCENDC_DUMP,该断言在 release 产物中被预处理为空宏,外部输入 numRanks_ 在发布形态下**零有效校验**。若 commContext 与 tiling 的 rankSize 不一致(两个独立来源,正是代码注释 :470-471 自证"必须一致性校验,否则…越界"),Kernel 将带非法值继续执行:numRanks_=0 时在 :504 sendersPerRank_ = halfBlocks / numRanks_、:544 tokenSlotSize_ = tokenArea / numRanks_ / NUM_SLOTS 触发设备侧除零 UB;numRanks_ > tilingData->rankSize 时 displs 区(:570-572 按 numRanks_ 规划)、statusBuf/tempBuf/indicesBuf 尺寸全面偏离 Host 按 tilingData->rankSize 规划的 workspace → GM 越界写。旧代码两条路径(debug abort / release 挂死)均不会发生内存越界,本 PR 造成 release 侧安全回退。 - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_grad_arch35.h 行 464-480): cpp aivId_ = GetBlockIdx(); totalBlocks_ = GetBlockNum(); tileBytes_ = TILE_BYTES; ctxPtr_ = (__gm__ EngramCommContext *)commContext; rankId_ = ctxPtr_->rankId; numRanks_ = ctxPtr_->rankSize; // commContext 为设备侧外部数据,与 Host 侧 tiling 的 rankSize(sendCounts.dim0/8)互为独立来源, // 必须一致性校验,否则 workspace 的 displs 区按 Host rankSize 规划而 Kernel 按 numRanks_ 写入会越界 ascendc_assert(numRanks_ != 0U && numRanks_ <= Mc2Kernel::MAX_QP_SIZE && numRanks_ == tilingData->rankSize, "invalid rankSize: commContext=%u, tiling=%u", numRanks_, tilingData->rankSize); channelsPerRank_ = ctxPtr_->channelsPerRank; if (channelsPerRank_ == 0) { channelsPerRank_ = 1; } (原代码为 if (numRanks_ == 0U || numRanks_ > MAX_QP_SIZE || numRanks_ != tilingData->rankSize) { RUNTIME_ABORT(...); },RUNTIME_ABORT 含 while(true) release 兜底) - **假设检验证据**: - 正向证据: | 证据类型 | 分值 | 证据描述 | |---------|------|---------| | 规范违反 | +40% | 外部输入必须有效校验;release 下校验被预处理为空,且 PR 移除了唯一 release 生效的 fail-stop 路径 | | PR 归属 | +20% | 变更在 diff 内 | | 数据流风险 | +15% | numRanks_ 直流除数 :504/:544 与 displs/status/temp/indices 尺寸 :570-572/:588/:600-613 | | 调用链风险 | +15% | 传入 uniqueScatter_/win counter 地址计算贯穿全程 | - 负向证据: | 证据类型 | 分值 | 证据描述 | |---------|------|---------| | 防御存在 | 0% | assert 在 release 产物中为空宏,校验有效性验证不通过,不得计分 | | 上游校验 | 0% | Host :276 校验的是 tiling 来源 rankSize,与 commContext 来源非同一变量,不覆盖当前风险变量 | - 自信值 = 40+20+15+15 = **90% ≥ 70% → 判定违规** - **修复建议**:对防越界的外部输入一致性校验保留确定性失败路径,不应用纯 ascendc_assert 承载。建议改为显式分支:校验失败时置错误标志走安全退出路径(如 numRanks_ 无效时跳过通信阶段直接写零输出并 return),或恢复带确定性终止语义的兜底(保留 RUNTIME_ABORT 形态仅用于此类安全校验,与纯日志性 assert 区分)。 ### [cpp-secure 1.2] 内存安全 —— RUNTIME_ABORT→ascendc_assert 后可达越界写 - **问题描述**:PR 将全部 8 处 RUNTIME_ABORT(assert 后 while(true) 无条件死旋,release 下仍确定性挂死、内存安全)替换为 ascendc_assert(release 空宏,已验证)。因此 shipping/release 构建下这些校验完全消失,失败后流程带着非法状态继续执行,产生可达的越界写。最严重一处:pendingHandles_ 固定数组(MAX_PENDING_HANDLES=8,声明于 :156)越界写——发送核 handle 数 = ⌈numRanks_/halfBlocks⌉(SendGradToPeers :859 步长 numSendCores_,SendGradRemote 每次 :957 追加 1 个),当 numRanks_ > 4×totalBlocks(Host 仅校验 rankSize ≤ 1024,tiling.cpp:276,无 rankSize 与核数关系约束;aivNum=48 时 rankSize ∈ (192,1024] 即触发)时 count 可达 ~43 ≫ 8,release 下 :956 直接写越界 280+ 字节,篡改相邻成员(isSender_/gradSortedGM_/recvGradGM_ 等 GM 指针)→ 后续任意 GM 写。次严重:numRanks_ 一致性校验失效(:472)后 displs 区写入越出 Host 规划的 workspace。对比 :474-477 channelsPerRank_ 仍保留 release 生效的 if 回退——同函数内正确的防御范式反衬此处弱化。 - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_grad_arch35.h 行 944-958): cpp int32_t ret = hcomm_.WriteWithNotifyNbi(handle, remoteSlotAddr, srcAddr, dataBytes, remoteCounterAddr, static_cast<uint64_t>(localWriteCnt + 1)); ascendc_assert(ret == 0, "WriteWithNotifyNbi failed, ret=%d, tag=ExTok_data, rankId=%u, dstRank=%u", ret, rankId_, dstRank); localWriteCnt++; totalSent += chunkLen; } RetireCreditCounter(); // 单核 remote handle 数随 numRanks_/numSendCores_ 配置增长,必须守卫固定数组边界 ascendc_assert(pendingHandleCount_ < Mc2Kernel::MAX_PENDING_HANDLES, "pendingHandles overflow: count=%u, max=%u, rankId=%u, dstRank=%u", pendingHandleCount_, Mc2Kernel::MAX_PENDING_HANDLES, rankId_, dstRank); pendingHandles_[pendingHandleCount_] = handle; pendingHandleCount_++; ` - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_ See merge request: cann/ops-transformer!11577 | 1 天前 | |
engram反向超时DFX Co-authored-by: luozhonglin222<luozhonglin1@huawei.com> # message auto-generated for no-merge-commit merge: !11577 merge master into master engram反向超时DFX Created-by: luozhonglin222 Commit-by: luozhonglin222 Merged-by: cann-robot Description: ## 描述 1.反向超时的DFX 2.UB溢出修复 3.scatter汇总和sort分核一致性修复 ## 关联的Issue <!-- 如果这个PR是为了解决特定的Issue,请在这里提供Issue链接。例如:关联Issue #000--> <!-- 如果这个PR是为了解决特定的问题单,请在这里描述问题单单号。--> ## 测试 <!--描述进行了哪些测试来验证你的改动。包括但不限于二级冒烟、算子泛化等。--> ## 文档更新 <!--如果这个PR包含文档的更新,请在这里指出。例如:更新了README.md文件。--> ## 类型标签 <!-- [x] 表示选中 --> - [x] 🐛 Bug修复 - [ ] ✨ 新特性 - [ ] ⚡ 性能优化 - [ ] ♻️ 重构 - [ ] 🧪 测试 - [ ] 📦 构建/CI - [ ] 🔧 配置变更 - [ ] 📝 文档更新 - [ ] ⬆️ 依赖升级 - [ ] 🔒 安全修复 - [ ] 🧹 代码清理 - [ ] ❓ 其他,请描述: PR #11577 代码检视报告 ## 检视概览 - **PR**: https://gitcode.com/cann/ops-transformer/pull/11577 - **算子**: engram_fetch_grad(mc2/engram_fetch_grad/) - **代码侧别**: 混合(Tiling 1 文件 + Kernel 4 文件) - **变更规模**: 5 个文件,有效变更约 207 行(+120 / -87) - **变更主题**: ① Host 侧新增 sort/unique 两阶段 UB 池峰值 fail-fast 校验(CalcSortUbBytes 镜像 Kernel GetUbSize),Kernel 侧 unique 阶段 entryBuf_ 由 64KB 收缩为 uniqueEntryBytes_;② 删除 RUNTIME_ABORT 宏与 COMM_RETRY_COUNT 重试,8 处失败点统一 ascendc_assert 直报,TimeoutCheck 增加 TimeoutSite;③ sort ProcessHist/ProcessScatter tile→core 归属由 batch-major 改 core-major;④ numRecvCores_ 扣除 flagCore 修正 - **检视文档**: cpp-secure / cpp-general / ascendc-api / ascendc-perf / ascendc-topk / mc2-specific / regbase-review-checks / simt-api-analysis(compile-secure / python-secure / cpp-style 按声明式匹配跳过) - **总条例数**: 135 条(32 组 / 6 波并行检视) - **检视时间**: 2026-09-09 - **配套产物**: code_summary.md(代码概要+变量溯源)、api_prestudy.md(API 约束预研) ## 检视统计 | 状态 | 条例数 | 占比 | |------|------|------| | PASS | 123 | 91.1% | | FAIL(发现问题)| 6 | 4.4% | | SUSPICIOUS(需关注/疑似)| 6 | 4.4% | | 范围外备注 | 1 | - | ## 核心结论 **6 个 FAIL 高度同源,可归并为 2 个根因:** - **根因 A(4 个 FAIL:cpp-secure 1.2/1.3/4.1、cpp-general 1.1/12.1)**:本 PR 删除 RUNTIME_ABORT 宏(含 while(true) release 兜底)并将 8 处失败点改为 ascendc_assert。经 CANN 9.0.0 头文件实证(kernel_operator_dump_tensor_intf.h:65-68),未定义 ASCENDC_DUMP 时 ascendc_assert 展开为**空宏**,且本算子构建未定义 ASCENDC_DUMP。release 产物下:commContext(设备侧外部数据)rankSize 一致性校验完全失效 → 除零 UB / workspace displs 越界写;pendingHandles 固定数组(8 元素)越界守卫失效;通信失败/超时静默继续(且删除了原有 3 次重试)。 - **根因 B(1 个 FAIL:cpp-secure 2.2)**:numRecvCores_==0 兜底分支中 totalBlocks_==1 时 numSendCores_ 由 0 改为 1,导致 recvIdx = aivId_ - numSendCores_ 在 aivId_=0 时发生 uint32 回绕,接收循环零迭代,单核形态多 rank 场景远端梯度静默丢失(功能回归)。 **正面确认**:sort tile 归属 core-major 改写修复了 tileOffsets 跨核免屏障假设失效的竞态(多方交叉验证一致);UB 池收缩(entryBuf_ 64KB→uniqueEntryBytes_)Host/Kernel 两侧公式逐项核算等值、构造性安全;新增两道 Host fail-fast 校验(sortUb 峰值 / minUniqueNeed)release 生效,覆盖 Host 可建模部分。 --- ## 发现问题(HIGH 置信度) ### [cpp-secure 2.2] 无符号回绕 —— recvIdx 回绕致单核形态接收零迭代 - **问题描述**:PR 修改 Init() 的单核 fallback(numSendCores_ 旧值 totalBlocks_-1U(totalBlocks_==1 时为 0)改为 1)后,RecvGradFromPeers 中无守卫的 uint32 减法 aivId_ - numSendCores_ 在 totalBlocks_==1 && numRanks_>1 时发生 0-1=0xFFFFFFFF 回绕。totalBlocks_==1 时 isReceiver_ 恒为 true(:525),回绕后的 recvIdx 使 for (wIdx = recvIdx; wIdx < totalWorkUnits; ...) 零迭代——本核静默跳过全部接收,对端发送核在 credit/tokenRead 计数器上自旋直至超时。旧代码该形态 numSendCores_=0、recvIdx=0 可完整覆盖(功能回归);Host 侧无 aivNum 与 rankSize 的关联校验,PR 又为 totalBlocks_<=1 专门设置了同核收发 fallback 分支,说明该形态是被明确支持的目标场景。 - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_grad_arch35.h 行 514-526): cpp numSendCores_ = sendersPerRank_ * numRanks_; if (numSendCores_ > halfBlocks) { numSendCores_ = halfBlocks; } numRecvCores_ = (totalBlocks_ > numSendCores_ + 1U) ? (totalBlocks_ - numSendCores_ - 1U) : 0U; if (numRecvCores_ == 0U) { numRecvCores_ = 1U; numSendCores_ = (totalBlocks_ > 1U) ? (totalBlocks_ - 1U) : 1U; // ← 本 PR 新增:totalBlocks_==1 时 numSendCores_=1 } isSender_ = (aivId_ < numSendCores_) || (totalBlocks_ <= 1U); isReceiver_ = (aivId_ >= numSendCores_ && aivId_ < totalBlocks_ - 1U) || (totalBlocks_ <= 1U); // totalBlocks_==1 时恒 true isFlagCore_ = (aivId_ == totalBlocks_ - 1U); - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_grad_arch35.h 行 968-988): ``cpp __aicore__ inline void EngramFetchGradArch35::RecvGradFromPeers() { if (!isReceiver_) { return; } GM_ADDR localWinBase = (GM_ADDR)ctxPtr_->commBuffer[rankId_]; uint32_t recvIdx = aivId_ - numSendCores_; // ← aivId_=0, numSendCores_=1 时回绕为 0xFFFFFFFF uint32_t totalWorkUnits = (numRanks_ - 1U) * sendersPerRank_; if (totalWorkUnits == 0U) { return; } // Each work unit must be owned by EXACTLY one receiver core: with numRecvCores_ > // totalWorkUnits, recvIdx % totalWorkUnits would map several cores onto the same // (srcRank, senderIdx) unit and race on the tokenRead counters (flow-control slots // get reused while the slower duplicate is still copying -> recvGrad corruption). for (uint32_t wIdx = recvIdx; wIdx < totalWorkUnits; wIdx += numRecvCores_) { // ← 0xFFFFFFFF < totalWorkUnits 恒假,零迭代 uint32_t adjustedSrcRank = wIdx / sendersPerRank_; ` - **假设检验证据**: - 正向证据: | 证据类型 | 分值 | 证据描述 | |---------|------|---------| | 规范违反 | +40% | uint32 减法无守卫,回绕实证(回绕值用作循环起始索引,命中条例"减法回绕"模式)| | 上下文防御缺失 | +30% | aivId_ < numSendCores_ 场景无钳位/分支防护 | | PR 归属 | +20% | 回绕可达性由本 PR :522 fallback 0→1 直接引入 | | 数据流风险 | +15% | numSendCores_ 值域被 PR 改变 | | 领域关联 | +10% | 领域规则交叉印证(TIL-1 独立检出同一问题)| - 负向证据: | 证据类型 | 分值 | 证据描述 | |---------|------|---------| | 防御存在 | -20% | 循环条件 wIdx < totalWorkUnits 兜住内存访问,后果限于功能失效而非越界 | - 自信值 = 40+30+20+15+10-20 = **95% ≥ 70% → 判定违规** - **修复建议**:fallback 分支保持 totalBlocks_==1 时 numSendCores_=0(维持旧覆盖行为),或将接收循环起点改为饱和计算 uint32_t recvIdx = (aivId_ > numSendCores_) ? (aivId_ - numSendCores_) : 0U;;同时建议在 fallback 中保证至少有一个核的 recvIdx 落在 [0, totalWorkUnits) 内。 ### [cpp-secure 4.1] 外部输入校验 —— rankSize 一致性校验 release 归零 - **问题描述**:PR 将 commContext(设备侧外部数据)rankSize 一致性校验的强制力由"release 下降级为确定性挂死(RUNTIME_ABORT 的 while(true) 兜底)"改为纯 ascendc_assert。已验证本算子构建不定义 ASCENDC_DUMP,该断言在 release 产物中被预处理为空宏,外部输入 numRanks_ 在发布形态下**零有效校验**。若 commContext 与 tiling 的 rankSize 不一致(两个独立来源,正是代码注释 :470-471 自证"必须一致性校验,否则…越界"),Kernel 将带非法值继续执行:numRanks_=0 时在 :504 sendersPerRank_ = halfBlocks / numRanks_、:544 tokenSlotSize_ = tokenArea / numRanks_ / NUM_SLOTS 触发设备侧除零 UB;numRanks_ > tilingData->rankSize 时 displs 区(:570-572 按 numRanks_ 规划)、statusBuf/tempBuf/indicesBuf 尺寸全面偏离 Host 按 tilingData->rankSize 规划的 workspace → GM 越界写。旧代码两条路径(debug abort / release 挂死)均不会发生内存越界,本 PR 造成 release 侧安全回退。 - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_grad_arch35.h 行 464-480): cpp aivId_ = GetBlockIdx(); totalBlocks_ = GetBlockNum(); tileBytes_ = TILE_BYTES; ctxPtr_ = (__gm__ EngramCommContext *)commContext; rankId_ = ctxPtr_->rankId; numRanks_ = ctxPtr_->rankSize; // commContext 为设备侧外部数据,与 Host 侧 tiling 的 rankSize(sendCounts.dim0/8)互为独立来源, // 必须一致性校验,否则 workspace 的 displs 区按 Host rankSize 规划而 Kernel 按 numRanks_ 写入会越界 ascendc_assert(numRanks_ != 0U && numRanks_ <= Mc2Kernel::MAX_QP_SIZE && numRanks_ == tilingData->rankSize, "invalid rankSize: commContext=%u, tiling=%u", numRanks_, tilingData->rankSize); channelsPerRank_ = ctxPtr_->channelsPerRank; if (channelsPerRank_ == 0) { channelsPerRank_ = 1; } (原代码为 if (numRanks_ == 0U || numRanks_ > MAX_QP_SIZE || numRanks_ != tilingData->rankSize) { RUNTIME_ABORT(...); },RUNTIME_ABORT 含 while(true) release 兜底) - **假设检验证据**: - 正向证据: | 证据类型 | 分值 | 证据描述 | |---------|------|---------| | 规范违反 | +40% | 外部输入必须有效校验;release 下校验被预处理为空,且 PR 移除了唯一 release 生效的 fail-stop 路径 | | PR 归属 | +20% | 变更在 diff 内 | | 数据流风险 | +15% | numRanks_ 直流除数 :504/:544 与 displs/status/temp/indices 尺寸 :570-572/:588/:600-613 | | 调用链风险 | +15% | 传入 uniqueScatter_/win counter 地址计算贯穿全程 | - 负向证据: | 证据类型 | 分值 | 证据描述 | |---------|------|---------| | 防御存在 | 0% | assert 在 release 产物中为空宏,校验有效性验证不通过,不得计分 | | 上游校验 | 0% | Host :276 校验的是 tiling 来源 rankSize,与 commContext 来源非同一变量,不覆盖当前风险变量 | - 自信值 = 40+20+15+15 = **90% ≥ 70% → 判定违规** - **修复建议**:对防越界的外部输入一致性校验保留确定性失败路径,不应用纯 ascendc_assert 承载。建议改为显式分支:校验失败时置错误标志走安全退出路径(如 numRanks_ 无效时跳过通信阶段直接写零输出并 return),或恢复带确定性终止语义的兜底(保留 RUNTIME_ABORT 形态仅用于此类安全校验,与纯日志性 assert 区分)。 ### [cpp-secure 1.2] 内存安全 —— RUNTIME_ABORT→ascendc_assert 后可达越界写 - **问题描述**:PR 将全部 8 处 RUNTIME_ABORT(assert 后 while(true) 无条件死旋,release 下仍确定性挂死、内存安全)替换为 ascendc_assert(release 空宏,已验证)。因此 shipping/release 构建下这些校验完全消失,失败后流程带着非法状态继续执行,产生可达的越界写。最严重一处:pendingHandles_ 固定数组(MAX_PENDING_HANDLES=8,声明于 :156)越界写——发送核 handle 数 = ⌈numRanks_/halfBlocks⌉(SendGradToPeers :859 步长 numSendCores_,SendGradRemote 每次 :957 追加 1 个),当 numRanks_ > 4×totalBlocks(Host 仅校验 rankSize ≤ 1024,tiling.cpp:276,无 rankSize 与核数关系约束;aivNum=48 时 rankSize ∈ (192,1024] 即触发)时 count 可达 ~43 ≫ 8,release 下 :956 直接写越界 280+ 字节,篡改相邻成员(isSender_/gradSortedGM_/recvGradGM_ 等 GM 指针)→ 后续任意 GM 写。次严重:numRanks_ 一致性校验失效(:472)后 displs 区写入越出 Host 规划的 workspace。对比 :474-477 channelsPerRank_ 仍保留 release 生效的 if 回退——同函数内正确的防御范式反衬此处弱化。 - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_grad_arch35.h 行 944-958): cpp int32_t ret = hcomm_.WriteWithNotifyNbi(handle, remoteSlotAddr, srcAddr, dataBytes, remoteCounterAddr, static_cast<uint64_t>(localWriteCnt + 1)); ascendc_assert(ret == 0, "WriteWithNotifyNbi failed, ret=%d, tag=ExTok_data, rankId=%u, dstRank=%u", ret, rankId_, dstRank); localWriteCnt++; totalSent += chunkLen; } RetireCreditCounter(); // 单核 remote handle 数随 numRanks_/numSendCores_ 配置增长,必须守卫固定数组边界 ascendc_assert(pendingHandleCount_ < Mc2Kernel::MAX_PENDING_HANDLES, "pendingHandles overflow: count=%u, max=%u, rankId=%u, dstRank=%u", pendingHandleCount_, Mc2Kernel::MAX_PENDING_HANDLES, rankId_, dstRank); pendingHandles_[pendingHandleCount_] = handle; pendingHandleCount_++; ` - **代码片段**(mc2/engram_fetch_grad/op_kernel/arch35/engram_fetch_ See merge request: cann/ops-transformer!11577 | 1 天前 | |
ElasticBuffer 修改hidden拦截校验 Co-authored-by: liuyibin<liuyibin5@huawei.com> # message auto-generated for no-merge-commit merge: !10990 merge hidden into master ElasticBuffer 修改hidden拦截校验 Created-by: liuyibin Commit-by: liuyibin Merged-by: cann-robot Description: ## 描述 ElasticBuffer 去掉对hidden的拦截 ## 关联的Issue <!-- 如果这个PR是为了解决特定的Issue,请在这里提供Issue链接。例如:关联Issue #000--> <!-- 如果这个PR是为了解决特定的问题单,请在这里描述问题单单号。--> ## 测试 <!--描述进行了哪些测试来验证你的改动。包括但不限于二级冒烟、算子泛化等。--> ## 文档更新 ElasticBuffer.md ## 类型标签 <!-- [x] 表示选中 --> - [ x] 🐛 Bug修复 - [ ] ✨ 新特性 - [ ] ⚡ 性能优化 - [ ] ♻️ 重构 - [ ] 🧪 测试 - [ ] 📦 构建/CI - [ ] 🔧 配置变更 - [x ] 📝 文档更新 - [ ] ⬆️ 依赖升级 - [ ] 🔒 安全修复 - [ ] 🧹 代码清理 - [ ] ❓ 其他,请描述: See merge request: cann/ops-transformer!10990 | 7 天前 | |
engram support training Co-authored-by: luozhonglin222<luozhonglin1@huawei.com> # message auto-generated for no-merge-commit merge: !8914 merge master into master engram support training Created-by: luozhonglin222 Commit-by: luozhonglin222 Merged-by: cann-robot Description: ## 描述 engram算子支持训练HOST代码,前向复用现有接口,增加相应参数,反向增加新接口 ## 关联的Issue https://gitcode.com/cann/ops-transformer/issues/3838 ## 测试 本地验证 ## 文档更新 不涉及 ## 类型标签 <!-- [x] 表示选中 --> - [ ] 🐛 Bug修复 - [x] ✨ 新特性 - [ ] ⚡ 性能优化 - [ ] ♻️ 重构 - [ ] 🧪 测试 - [ ] 📦 构建/CI - [ ] 🔧 配置变更 - [ ] 📝 文档更新 - [ ] ⬆️ 依赖升级 - [ ] 🔒 安全修复 - [ ] 🧹 代码清理 - [ ] ❓ 其他,请描述: # PR #8914 代码检视报告 ## 检视概览 - **PR URL**:https://gitcode.com/cann/ops-transformer/pull/8914 - **算子名**:engram_fetch_grad / engram_fetch_train / elastic_buffer - **代码侧别**:Kernel + Tiling + 混合(torch_extension) - **变更文件数**:21 - **文件组数**:6(K1, K2, H1, H2, S, E) - **总条例数**:346 - **检视时间**:2026-07-21 - **检视模式**:大型 PR 检视(21 文件 > 10 阈值) ## 检视统计 | 状态 | 条例数 | 占比 | |------|--------|------| | PASS | 341 | 98.6% | | FAIL(发现问题) | 5 | 1.4% | | SUSPICIOUS(需关注) | 0 | 0% | ### 逐文件组统计 | 文件组 | 侧别 | 文件数 | PASS | FAIL | |--------|------|--------|------|------| | engram_fetch_grad_kernel_G1 (K1) | Kernel | 3 | 27 | 0 | | engram_fetch_train_kernel_G1 (K2) | Kernel | 3 | 37 | 0 | | engram_fetch_grad_host_G1 (H1) | Tiling | 3 | 66 | 2 | | engram_fetch_train_host_G1 (H2) | Tiling | 3 | 70 | 2 | | shared (S) | 混合 | 6 | 61 | 1 | | elastic_buffer (E) | 混合 | 3 | 80 | 0 | | **合计** | — | **21** | **341** | **5** | ### 系统性风险 **SEC-4.1 / TOPK-7 在 H1 和 H2 中均失败**:两个 Tiling 侧文件组(engram_fetch_grad 和 engram_fetch_train)均存在 sendCounts/recvLocalEntry(或 sendCountsOut/recvLocalEntryOut)的 dim(0) 值未做非负/范围校验的问题。两个算子的 tiling 代码结构高度相似,校验缺失模式相同,建议全局排查并统一修复。 --- ## 发现问题(HIGH 置信度) ### 1. [engram_fetch_grad_host_G1] SEC-4.1 外部输入数据需要做合法性校验 - **问题描述**:sendCounts 的 dim(0) 和 recvLocalEntry 的 dim(0) 作为外部输入(来自 graph shape)仅校验了维度数(1D),未对 dim(0) 的值做合法性校验,违反 SEC-4.1「外部输入数据需要做合法性校验且确保校验范围正确」。同一函数中 commContext(行 206 GetDim(0) <= 0)和 gradFetched(行 221 numTokens < 0、行 226 hiddenDim <= 0)均对 dim 值做了显式校验,sendCounts/recvLocalEntry 缺失同类校验,形成不一致的安全缺口。 **风险链路 1(sendCounts)**:dim(0) 若为负值或超过 UINT32_MAX,行 260 static_cast<uint32_t> 会发生窄化截断/回绕,rankSize 变为巨大错误值 → SetBlockDim 和 workspace 计算均受影响。 **风险链路 2(recvLocalEntry)**:dim(0) 赋值给 int64_t totalRecv(行 281),若为负值则保留负数 → wsRecvGrad = totalRecv * hiddenBytes 为负 → wsTotal 求和可能为负 → static_cast<size_t>(wsTotal) 将负数回绕为极大 size_t 值,导致 workspace 申请异常。 - **代码片段**(mc2/engram_fetch_grad/op_host/op_tiling/engram_fetch_grad_tiling.cpp 行 251-281): cpp // sendCounts: 1D (W,) const gert::StorageShape *sendCountsShape = context->GetInputShape(IN_SEND_COUNTS); OP_TILING_CHECK(sendCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCounts", (std::to_string(sendCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCounts must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsShape->GetStorageShape().GetDim(0)); // recvCounts: 1D (W,) const gert::StorageShape *recvCountsShape = context->GetInputShape(IN_RECV_COUNTS); OP_TILING_CHECK(recvCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvCounts", (std::to_string(recvCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvCounts must be 1D."), return ge::GRAPH_FAILED); // recvLocalEntry: 1D (R,) const gert::StorageShape *recvLocalEntryShape = context->GetInputShape(IN_RECV_LOCAL_ENTRY); OP_TILING_CHECK(recvLocalEntryShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvLocalEntry"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvLocalEntryShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvLocalEntry", (std::to_string(recvLocalEntryShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvLocalEntry must be 1D."), return ge::GRAPH_FAILED); totalRecv = recvLocalEntryShape->GetStorageShape().GetDim(0); - **假设检验证据**: - (+40%) 规范违反:SEC-4.1 要求外部输入数据需做合法性校验,sendCounts dim(0) 直接 cast uint32_t、recvLocalEntry dim(0) 直接赋值 int64_t,均无值范围校验 - (+30%) 上下文防御缺失:同函数内 commContext/gradFetched 均有非负校验,唯独 sendCounts/recvLocalEntry 缺失 - (+20%) PR 归属:新增文件,全部行在 diff 变更范围内 - (+15%) 数据流风险:rankSize 负值回绕 / totalRecv 负值导致 workspace 回绕为巨大值 - 自信值 = 105% ≥ 70% → 判定违规 - **修复建议**: 1. 在行 260 之前增加 sendCounts dim(0) 值校验(> 0) 2. 在行 281 之前增加 recvLocalEntry dim(0) 值校验(>= 0) 3. 参照同函数内 commContext/gradFetched 的校验模式 --- ### 2. [engram_fetch_grad_host_G1] TOPK-7 融合规则/InferShape/Tiling 外部输入校验 - **问题描述**:TOPK-7 要求「Tiling 外部输入使用时必须进行合法性校验」,校验内容包括「Shape 维度是否合法(不为负数、不超过限制)」。sendCounts dim(0) 和 recvLocalEntry dim(0) 作为外部输入被提取后直接使用,未做非负/范围校验。同函数内 commContext dim0(<=0 校验)、gradFetched dim0(<0 校验)、hiddenDim(<=0 校验)均有非负防御,唯独 sendCounts 和 recvLocalEntry 缺失。此问题与 SEC-4.1 同构。 - **代码片段**(mc2/engram_fetch_grad/op_host/op_tiling/engram_fetch_grad_tiling.cpp 行 251-281): cpp // sendCounts: 1D (W,) const gert::StorageShape *sendCountsShape = context->GetInputShape(IN_SEND_COUNTS); OP_TILING_CHECK(sendCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCounts", (std::to_string(sendCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCounts must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsShape->GetStorageShape().GetDim(0)); // ...(recvLocalEntry 同理) totalRecv = recvLocalEntryShape->GetStorageShape().GetDim(0); - **假设检验证据**: - (+40%) 规范违反:TOPK-7 要求 Shape 维度不为负数 - (+30%) 上下文防御缺失:同函数内其他 tensor 均有非负校验 - (+20%) PR 归属:新增文件 - (+15%) 数据流风险:负值 dim0 经 static_cast 回绕或以负 int64_t 传播至 workspace 计算 - 自信值 = 105% ≥ 70% → 判定违规 - **修复建议**:与 SEC-4.1 同一问题,修复方案一致。 --- ### 3. [engram_fetch_train_host_G1] SEC-4.1 外部输入数据需要做合法性校验 - **问题描述**:CheckTensorDim 中 sendCountsOut 和 recvLocalEntryOut 的 dim0 值作为外部输入未做范围校验。rankSize(uint32_t)通过 static_cast 从 int64_t 缩窄转换,若 dim0 为负或超过 UINT32_MAX 将静默截断/回绕为巨大值;totalRecv(int64_t)未校验非负,若为负值将导致 wsLocalData = totalRecv * hiddenBytes 为负,进而 wsTotal 为负,再经 static_cast<size_t>(wsTotal) 回绕为约 1.8e19 的巨大 workspace 申请。同函数内 commContextShape dim0 > 0(行 188)、numTokens >= 0(行 202)、numRecvOutShape dim0 == 1(行 272)均已校验,此处校验明显不一致。 - **代码片段**(mc2/engram_fetch_train/op_host/op_tiling/engram_fetch_train_tiling.cpp 行 235-262): cpp const gert::StorageShape *sendCountsOutShape = context->GetOutputShape(OUT_SEND_COUNTS); OP_TILING_CHECK(sendCountsOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCountsOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCountsOut", (std::to_string(sendCountsOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCountsOut must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsOutShape->GetStorageShape().GetDim(0)); const gert::StorageShape *recvCountsOutShape = context->GetOutputShape(OUT_RECV_COUNTS); OP_TILING_CHECK(recvCountsOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvCountsOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvCountsOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvCountsOut", (std::to_string(recvCountsOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvCountsOut must be 1D."), return ge::GRAPH_FAILED); const gert::StorageShape *recvLocalEntryOutShape = context->GetOutputShape(OUT_RECV_LOCAL_ENTRY); OP_TILING_CHECK(recvLocalEntryOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvLocalEntryOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvLocalEntryOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvLocalEntryOut", (std::to_string(recvLocalEntryOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvLocalEntryOut must be 1D."), return ge::GRAPH_FAILED); totalRecv = recvLocalEntryOutShape->GetStorageShape().GetDim(0); - **假设检验证据**: - (+40%) 规范违反:外部 shape dim 值未做范围/非负校验 - (+30%) 上下文防御缺失:作用域内仅校验 dim 数,无 dim 值范围校验 - (+20%) PR 归属:新文件,全量代码均在 diff 范围 - (+15%) 数据流风险:rankSize 缩窄截断 / totalRecv 负值 → workspace 回绕 - 自信值 = 105% ≥ 70% → 判定违规 - **修复建议**: 1. 在行 243 前增加 sendCountsOut dim(0) 非负及 <= UINT32_MAX 校验 2. 在行 262 前增加 recvLocalEntryOut dim(0) >= 0 校验 3. 参照同函数内 numTokens(行 202-205)的校验模式 --- ### 4. [engram_fetch_train_host_G1] TOPK-7 融合规则/InferShape/Tiling 外部输入校验 - **问题描述**:与 SEC-4.1 同一问题。TOPK-7 要求 Tiling 外部输入的 Shape 维度不为负数、不超过限制。sendCountsOut dim0(赋值给 rankSize,缩窄为 uint32_t)和 recvLocalEntryOut dim0(赋值给 totalRecv)均未做非负/范围校验。同函数内 commContextShape dim0 > 0(行 188)、indicesShape numTokens >= 0(行 202)均已校验,存在校验不一致。 - **代码片段**(mc2/engram_fetch_train/op_host/op_tiling/engram_fetch_train_tiling.cpp 行 235-262):(同 SEC-4.1) - **假设检验证据**: - (+40%) 规范违反:Tiling 外部 shape dim 值未做非负/范围校验 - (+30%) 上下文防御缺失:仅校验 dim 数无 dim 值校验 - (+20%) PR 归属:新文件全量在 diff 范围 - (+15%) 数据流风险:rankSize 缩窄截断 / totalRecv 负值 → work See merge request: cann/ops-transformer!8914 | 1 个月前 |