已关闭
[Bug-Report|缺陷反馈]: CcuKernelMgr::GetKernel 并发读取 kernelMap_ 时同步保护可能不足 #286
MBpanzz创建于  6月24日关闭于  7月8日
MBpanzz
6月24日 创建

Thanks for sending an issue! Please fill in the following template to help quickly solve your problem.

Describe the current behavior / 问题描述 (Mandatory / 必填)

CcuKernelMgr::GetKernel() 中,函数直接读取成员变量 kernelMap_ 而未加锁保护。同时 UnRegister() 在互斥锁 kernelMapMutex_ 的保护下对 kernelMap_ 执行节点删除操作。异常回调路径 CcuTaskException::InitChannelMap 在另一把全局锁 g_channelMapMutex 下调用 GetKernel(),但该锁与 kernelMapMutex_ 不同,不能防止对 kernelMap_ 的并发访问。

当异常回调线程与主线程的 UnRegister() 并发执行时,GetKernel() 可能读到处于中间状态的 unordered_map,存在未定义行为的风险。此外,GetKernel() 返回的是 CcuKernel 裸指针,若调用侧在锁外使用该指针而同一时刻 UnRegister() 已销毁对应的 unique_ptr,可能出现 use-after-free。

相关的代码路径:

GetKernel(读取端,无锁):

// ccu_kernel_mgr.cc:730
CcuKernel *CcuKernelMgr::GetKernel(const CcuKernelHandle kernelHandle)
{
    auto it = kernelMap_.find(kernelHandle);  // ← 无锁读取
    if (it == kernelMap_.end()) {
        return nullptr;
    }
    return it->second.get();                  // ← 返回裸指针,锁外使用
}

UnRegister(删除端,持 kernelMapMutex_):

// ccu_kernel_mgr.cc:548-561
CcuResult CcuKernelMgr::UnRegister(const CcuKernelHandle kernelHandle)
{
    std::unique_lock<std::mutex> lock(kernelMapMutex_);
    ...
    kernelMap_.erase(kernelHandle);           // ← 销毁 unique_ptr,回收 kernel 对象
    return CcuResult::CCU_SUCCESS;
}

异常回调路径调用 GetKernel(持另一把锁):

// ccuTaskException.cc:228-235
HcclResult CcuTaskException::InitChannelMap(s32 deviceId, u64 ccuKernelHandle)
{
    std::lock_guard<std::mutex> lock(g_channelMapMutex);  // 全局锁,非 kernelMapMutex_
    auto &kernelMgr = hcomm::CcuKernelMgr::GetInstance(deviceId);
    auto *kernel = kernelMgr.GetKernel(ccuKernelHandle);  // ← 无锁读 kernelMap_
    ...
}

GetKernel 在 Launch 路径上的调用(同线程,安全):

// ccu_launch.cc:304-306
const uint32_t devLogicId = HcclGetThreadDeviceId();
auto &kernelMgr = hcomm::CcuKernelMgr::GetInstance(devLogicId);
auto *kernel = kernelMgr.GetKernel(kernelHandle);          // 与 Register 同线程,当前安全

Environment / 环境信息 (Mandatory / 必填)

该问题与特定运行环境无关

Steps to reproduce the issue / 重现步骤 (Mandatory / 必填)

  1. 在测试中创建多个 CCU kernel,在部分线程上反复执行 HcommCcuKernelLaunch 及后续的 CcuInstance 析构以触发 UnRegister
  2. 同时通过注入手段模拟 CCU 任务异常,使 CcuTaskException::ProcessCcuException 被频繁调用
  3. GetKernel()UnRegister() 入口处添加 ThreadSanitizer 或 std::atomic_thread_fence 检测,编译并运行
  4. 观察是否出现 kernelMap_ 相关的 data race 或 use-after-free 报告

Describe the expected behavior / 预期结果 (Mandatory / 必填)

GetKernel() 不应与 UnRegister() 并发访问同一 kernelMap_

Special notes for this issue/备注 (Optional / 选填)

likedislike
LLeewis成员
6月25日 关联了看板:HCCL
Leewis成员
6月25日 评论:

@MBpanzz 感谢对应社区的关注与反馈,当前该问题已收到,待分析明确后与您反馈;

likedislike
LLeewis成员
6月27日 issue类型由 任务 改变为 缺陷
Leewis成员
7月8日 评论:

您好,相关问题已合入,当前issue将闭环,后续有相关问题欢迎提交issue交流讨论。
https://gitcode.com/cann/hcomm/pull/3119

likedislike
LLeewis成员
7月8日 issue状态由 待办的 改变为 已完成
LLeewis成员
7月8日 关闭了 issue
CANN-robotCANN-robot成员
7月8日 添加了label:resolved
LLeewis成员
18 天前 移除了看板:HCCL