已关闭
[Bug] ClusterNodes中部分函数,在持有共享锁的情况下,对数据进行写操作 #169
wenjinhust创建于  6月22日关闭于  6月27日
wenjinhust
wenjinhust
6月22日 创建

Thanks for sending an issue! Please fill in the following template to help quickly solve your problem.
感谢您提交issue!请填写以下模板,以便快速解决您的问题。

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

MM-007 coordinatorClusterNodesshared_lock 下写共享状态,存在真实竞态和崩溃风险

1. 摘要

ClusterNodes 使用 std::shared_mutex 保护实例状态、故障集合和 virtualId 映射,但多个写路径错误地在 std::shared_lock<std::shared_mutex> 下执行 inserteraseoperator[] 和对象字段写入。与此同时,请求转发、调度和实例查询路径又长期并发读取同一批共享容器。

这不是“锁粒度可以更优”的代码味道,而是标准意义上的锁语义错误:读锁下写共享状态,遇到并发刷新、故障标记和请求读取时会进入未定义行为,结果可能是状态损坏、错误故障标记、崩溃或错误路由。

  • 严重性:高危
  • severity_reason:问题位于 coordinator 核心共享状态对象,触发后影响范围覆盖实例视图、故障恢复和请求路由,最坏可导致 coordinator 崩溃或持续错误调度。

2. 影响面与前置条件

  • 受影响组件:mindie_motor/src/coordinator/cluster_monitor/ClusterNodes.cpp
  • 主要入口:
    • 实例刷新链
    • 故障节点标记/移除链
    • 请求转发和状态读取链
  • 前置条件:
    • coordinator 在正常运行中并发处理实例刷新、故障恢复和请求读取
    • 不需要攻击者先控制本地文件或测试环境

3. 攻击路径

  1. 一条线程在实例刷新或故障恢复链中进入 ClusterNodes
  2. 代码在 shared_lock 持有期间修改 faultVirtualIdsfaultIdsvirtualToIdsMapInstanceInfo
  3. 同时,其他线程继续在读路径下读取相同容器和字段
  4. 容器重哈希、擦除或对象字段写入与并发读取交错,进入未定义行为
  5. 最终表现为状态损坏、错误故障追踪、崩溃或错误路由

4. 关键代码分析

写路径 1:

void ClusterNodes::AddFaultNode(uint64_t id)
{
    std::shared_lock<std::shared_mutex> lock(mtx);
    ...
    faultVirtualIds.insert(virtualId);
    faultIds.insert(id);
    virtualIdToDelTimeMap[virtualId] = system_clock::now();
    idToDelTimeMap[id] = system_clock::now();
}

写路径 2:

void ClusterNodes::RemoveFaultNode(uint64_t id)
{
    std::shared_lock<std::shared_mutex> lock(mtx);
    ...
    virtualIdToDelTimeMap.erase(virtualId);
    faultVirtualIds.erase(virtualId);
    auto allIds = virtualToIdsMap[virtualId];
    ...
    virtualToIdsMap.erase(virtualId);
}

写路径 3:

void ClusterNodes::UpdateExtraInfo(...)
{
    std::shared_lock<std::shared_mutex> lock(mtx);
    ...
    iter->second->metricPort = httpParam.first;
    iter->second->interCommPort = httpParam.second;
    iter->second->virtualId = virtualId;
    if (virtualToIdsMap.find(virtualId) == virtualToIdsMap.end()) {
        virtualToIdsMap[virtualId] = initSet;
    } else {
        virtualToIdsMap[virtualId].insert(id);
    }
}

位置:mindie_motor/src/coordinator/cluster_monitor/ClusterNodes.cpp:123-163,202-223

这些写路径与大量读路径并发共享同一把 mtx,例如 GetIp()GetPort()GetRole()GetInstanceInfos()GetVirtualIdToIds() 等。

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

Any

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

最小验证方向:

  1. 一个线程循环触发实例刷新和 UpdateExtraInfo()
  2. 另一个线程循环触发故障标记/移除
  3. 并发线程持续读取 GetIp()GetPort()GetVirtualIdToIds()
  4. 使用 TSAN 或并发 stress harness 观察 data race、崩溃或容器状态异常

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

9. 修复建议

  1. AddFaultNode()RemoveFaultNode()UpdateExtraInfo() 全部改为 std::unique_lock<std::shared_mutex>
  2. 复核同文件其余方法,排查是否还有“共享锁下写状态”的同类错误。
  3. ClusterNodes 增加 TSAN 回归或最小并发 stress 测试。
  4. virtualToIdsMap、fault 集合和 InstanceInfo 写入建立统一的写路径约束。

参见上文

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

likedislike
ascend-robotascend-robot成员
6月22日 添加了label:bug
wangyangwangyang成员
6月27日 关闭了 issue
ascend-robotascend-robot成员
6月27日 添加了label:resolved