Prhub

#54303 [Rust Frontend] Bound recursive argument parsers

原始 PR 作者 BugenZhao 合并时间 2026-09-02 03:45 文件变更 4 提交数 1 评论 2 代码增减 +139 / -3

执行摘要

为 Rust 解析器中的递归参数解析添加深度限制,防止栈溢出。

修复 Issue #50927 中报告的栈溢出崩溃:Gemma4 统一解析器的递归下降语法没有深度限制,导致深度嵌套的模型输出耗尽工作线程栈并终止进程。仅需约 6 KB 的生成文本即可触发崩溃,严重影响服务稳定性。

值得精读,展示了在 Rust 解析器中应用 RAII 守卫防止栈溢出的简洁设计。关键决策包括线程本地存储和 128 深度限制的选择,适用于类似递归场景。

讨论亮点

Review 讨论极少,仅有 claude[bot] 的自动化评论和 njhill 的批准('Thanks @BugenZhao')。未发现技术争议或设计讨论,表明方案清晰且被社区接受。

实现拆解

  1. 新增递归守卫模块:创建 rust/src/parser/src/utils/recursion.rs,定义 ParserRecursionGuard 结构体,使用线程本地存储跟踪递归深度,当达到上限(128)时返回 Cut 错误。
  2. 集成守卫到解析器:在 rust/src/parser/src/utils.rs 中引入 recursion 模块;在 gemma4.rs 的 gemma4_object 和 gemma4_array_value 函数中,在解析对象/数组开头后调用守卫;在 minimax_m3.rs 的 parameter_element 函数中,解析元素前调用守卫。
  3. 添加测试覆盖:为守卫自身添加单元测试验证深度限制和 unwind 恢复;为 Gemma4 和 MiniMax M3 解析器添加测试验证递归限制生效。
  4. 配套改动:无配置或部署变更,仅涉及解析器内部逻辑和测试。
文件 模块 状态 重要度
rust/src/parser/src/utils/recursion.rs 递归守卫 added 8.75
rust/src/parser/src/unified/gemma4.rs Gemma4 解析器 modified 6.68
rust/src/parser/src/tool/minimax_m3.rs MiniMax M3 解析器 modified 6.91
rust/src/parser/src/utils.rs 解析器工具 modified 4.32

关键符号

ParserRecursionGuard::enter gemma4_object gemma4_array_value parameter_element

关键源码片段

rust/src/parser/src/utils/recursion.rs dependency-wiring

新增核心守卫模块,定义递归深度限制和 RAII 逻辑

// 递归深度守卫,使用线程本地存储跟踪当前递归层级
pub(crate) struct ParserRecursionGuard {
    previous_depth: usize,
    // 标记非 Send,确保守卫在创建线程上 drop
    _not_send: PhantomData<Rc<()>>,
}
​
impl ParserRecursionGuard {
    // 进入递归帧,超限返回 Cut 错误
    pub(crate) fn enter() -> ModalResult<Self> {
        PARSER_RECURSION_DEPTH.with(|depth| {
            let previous_depth = depth.get();
            if previous_depth >= MAX_PARSER_RECURSION_DEPTH {
                let mut error = ContextError::new();
                error.push(StrContext::Label("parser recursion limit exceeded"));
                return Err(ErrMode::Cut(error));
            }
            depth.set(previous_depth + 1);
            Ok(Self { previous_depth, _not_send: PhantomData })
        })
    }
}
​
// Drop 时恢复前一层深度
impl Drop for ParserRecursionGuard {
    fn drop(&mut self) {
        PARSER_RECURSION_DEPTH.with(|depth| depth.set(self.previous_depth));
    }
}
rust/src/parser/src/unified/gemma4.rs core-logic

修改 Gemma4 解析器,在对象和数组解析中集成守卫

// 解析嵌套 Gemma4 对象,添加递归深度检查
fn gemma4_object(input: &mut &str) -> ModalResult<Map<String, Value>> {
    literal("{").parse_next(input)?;
    let _guard = ParserRecursionGuard::enter()?; // 进入递归帧
    terminated(gemma4_args, literal("}")).parse_next(input)
}
​
// 解析 Gemma4 数组值,添加递归深度检查
fn gemma4_array_value(input: &mut &str) -> ModalResult<Vec<Value>> {
    literal("[").parse_next(input)?;
    let _guard = ParserRecursionGuard::enter()?; // 进入递归帧
    terminated(gemma4_array_content, literal("]")).parse_next(input)
}

评论区精华

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

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

风险与影响

  1. 兼容性风险:深度限制可能拒绝合法但嵌套极深的模型输出,但 128 层在实践中足够,且错误信息明确。
  2. 性能影响:守卫引入轻微开销(线程本地访问和分支),但解析器非热路径,影响可忽略。
  3. 测试覆盖:已添加单元测试验证边界和 unwind 恢复,但未覆盖所有解析路径的集成场景。

影响使用 Gemma4 或 MiniMax M3 模型工具调用的用户,防止服务因畸形输出崩溃。提升系统健壮性和安全性,无需用户变更代码。

栈溢出修复 递归深度限制 解析器崩溃防止

关联 Issue

#50927 [Bug]: stack overflow causing system crash: Rust unbounded recursion in the Gemma4 unified parser

完整报告

参与讨论