Prhub

#26313 Fix stale forward_metadata leak in DP attn unpadded idle batch

原始 PR 作者 ispobock 合并时间 2026-05-26 07:04 文件变更 1 提交数 1 评论 13 代码增减 +9 / -3

执行摘要

修复 DP Attention 空闲批次 stale metadata 引发的 UAF

PR #26157 添加的调试 canary 在 DSv4 分解 + DP Attention 场景下触发了 mapping_stack 的哨兵值,表明存在 use-after-free。进一步分析发现,当 batch_size == 0 时 forward_idle 未初始化 forward_metadata,导致 stale metadata 被后续 forward 使用,引发 gsm8k 精度从 0.975 骤降至 0.010。该问题仅 DP Attention 可复现,EAGLE、PD 分解等均非必要条件。

这是一个高价值、低风险的关键 bugfix,建议快速合入。根因分析详实,可作为调试复杂并发 bug 的范例。

讨论亮点

无 review 评论,仅通过 CI 验证。作者在原 PR body 中详细分析了根因链条,并提供了精度恢复数据(gsm8k: 0.010→0.975)。

实现拆解

python/sglang/srt/model_executor/model_runner.pyforward_idle 方法中,增加 else 分支:当 forward_batch.batch_size == 0 时,将 self.attn_backend.forward_metadata 显式设为 None

  • 变更前:只有 batch_size > 0 时调用 init_forward_metadatabatch_size == 0 时什么也不做,残留上次 forward 的 metadata。
  • 变更后batch_size == 0 时手动清除,避免下游 _maybe_upgrade_forward_metadata 误用。
    • 该修改是安全的,因为 AttentionBackend.forward 在 idle 模式下会提前返回,不会解引用 None 的 metadata;_maybe_upgrade_forward_metadataNone 也会直接返回。
文件 模块 状态 重要度
python/sglang/srt/model_executor/model_runner.py 执行引擎 modified 6.95

关键符号

forward_idle

关键源码片段

python/sglang/srt/model_executor/model_runner.py data-contract

唯一的变更文件,修改了 forward_idle 方法,在 unpadded idle 分支清除 stale forward_metadata。

# python/sglang/srt/model_executor/model_runner.py
# 修复 DP Attention 中 unpadded idle batch 的 stale metadata 问题def forward_idle(
    self, forward_batch: ForwardBatch, pp_proxy_tensors=None
) -> Union[LogitsProcessorOutput, PPProxyTensors]:
    # 在 DP Attention 中,idle batch 可能被填充(batch_size > 0)用于 MLP 同步。
    # 填充时需要重新初始化 metadata,否则 attention kernel 看到的 batch_size 不对。
    # 对于未填充的情况(batch_size == 0),显式丢弃前一次 forward 留下的 stale metadata。
    # 否则 DeepseekV4Model.forward 中的 _maybe_upgrade_forward_metadata 会误用
    # 旧 batch 的 req_pool_indices,导致 SWA mapping 的 use-after-free。
    #
    # 安全说明:AttentionBackend.forward 在 idle 模式会提前返回,不会解引用 None;
    # _maybe_upgrade_forward_metadata 对 None 也直接返回。
    if forward_batch.batch_size > 0:
        self.attn_backend.init_forward_metadata(forward_batch)
    else:
        # 将 forward_metadata 设为 None,防止下游误用
        self.attn_backend.forward_metadata = None
​
    kwargs = {}
    if self.support_pp:
        kwargs["pp_proxy_tensors"] = pp_proxy_tensors
    ctx = (
        self.device_timer.wrap(metadata={"category": "idle"})
        if self.device_timer
        else contextlib.nullcontext()
    )
    with ctx:
        return self.model.forward(
            forward_batch.input_ids,
            forward_batch.positions,
            forward_batch,
            **kwargs,
        )

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

风险较低。修改仅 2 行,且通过条件分支保护,不会影响正常 forward 路径。非 DP Attention 后端已有 idle 早期返回保护,None 值不会被解引用。对于 DSv4 后端,_maybe_upgrade_forward_metadataNone 类型检查会失败,直接返回。但需注意:如果未来新增后端在 idle 分支中假设 forward_metadata 不为 None,则可能引入新的问题。

直接影响 DSv4 模型在 DP Attention 分解推理场景的正确性和稳定性(gsm8k 精度从 0.010 恢复到 0.975);间接消除了一个潜在的跨 rank use-after-free 漏洞。对其他模型和后端无影响。

核心路径变更 并发安全风险

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论