写过 Rust 的人都踩过这个坑——编译器悄悄往你的程序里塞了一个空指针,1.98.1 今天紧急修了

写过 Rust 的人都清楚,这门语言最大的承诺就是「编译器替你保证安全」——不用操心空指针,不用担心数据竞争,编译器会拦住一切。

结果 Rust 1.98.0 告诉你:这个保证本身可以被编译器自己打破。

2026 年 8 月 20 日,Rust 1.98.0 稳定版发布,带来了 C 可变参数函数定义、128 位整数内联汇编、代数浮点数方法等新特性。但同一时间,一个严重的回归 bug 悄悄混入了 release。

问题是什么

GitHub Issue #161441 记录了这个故事。rama 项目在 aarch64-apple-darwin 平台上突然出现 SIGSEGV(段错误),稳定复现。调查后发现:Rust 1.98.0 编译器在某些情况下生成的 trait object vtable(虚函数表)中,偏移量 0x18 处的函数指针条目变成了空指针。当代码通过这个 vtable 槽位进行动态分发调用时,跳转到了零地址,程序直接崩溃。

这不是应用代码的 bug,是编译器生成的错误代码。更准确地说:这是 Rust 语言安全性承诺的核心地带被击穿了——trait object 的动态分发是 Rust 抽象的核心机制,而负责生成这段代码的 rustc 自己搞砸了。

更棘手的是,这个 bug 没有任何明显症状。你不会在编译时报错,rustc 也不会警告,它只是安静地往二进制里塞了一个空指针。程序可能在测试环境正常,跑上几个月也不一定崩——直到某个特定的条件路径触发那个 vtable 槽位。

为什么这比普通 bug 更严重

典型的编译器 bug 影响范围有限,比如某个优化 pass 搞坏了某段代码,定位到修复就行。但 vtable 生成错误的危险在于:它破坏的是 Rust 语言最基础的安全契约

Rust 的内存安全靠的是所有权和生命周期系统,但这套系统的前提是编译器生成的代码和语义规范一致。当编译器自己生成 undefined behavior 时,整个 safety net 都不成立了——因为你不知道还有多少其他地方的代码也在被错误地生成,哪怕现在没崩,也不是真的安全。

这就是为什么安全研究员会警告:某些 undefined behavior 在实践中可能不仅仅是 segfault,还可能被利用产生更严重的后果。UB 就是 UB,编译器引入的 UB 和应用代码引入的 UB 在这一点上没有区别。

两周内两个 P-critical 补丁

Rust 1.98.1 在 9 月 3 日紧急发布,只有一个改动:修复 vtable 误编译。

这不是孤例。8 月中旬的 Rust 1.97.1 也修复了一个 LLVM 误编译问题。连续两个 stable 周期都出现了安全关键的编译器补丁,且从发现到发布的周转时间都是两周左右——这个发布基础设施本身是值得信赖的,但编译器连续出 P-critical 问题本身值得警觉。

怎么办

没有任何 workaround。不要试图在应用层绕过这个问题——你没法通过改代码让编译器生成正确的 vtable。

一行命令:

rustup update stable
rustc --version
# 应该显示 rustc 1.98.1 (2026-09-03)

如果你的 CI 使用 Docker 镜像或固定工具链版本,现在就要更新 base image 的 Rust 版本。检查所有使用 dyn Trait 的生产代码,特别是通过 trait object 做动态分发的热点路径——这些是受影响的区域。

同时建议在 CI 里加入 rustc 版本校验,确保不会再跑进旧版本编译的路径。

真正让人不安的是什么

Rust 之所以在系统编程领域有今天的地位,正是因为它把「安全」从运行时兜底变成了编译时证明。这是一个强大的承诺,也是一把双刃剑——一旦编译器本身成为破坏这个承诺的源头,信任重建的成本会比任何一次生产事故都高。

这不是 Rust 的终点,但这件事给所有在生产环境依赖 Rust 的团队提了一个醒:工具链和语言本身一样需要版本控制和审计。

评论区

0 条评论

登录后可评论。

铁锈·Rust工具链 10 阅读