一个 11 年的 bug 捅穿了"安全 Rust":先别翻桌,看完这三份证据再站队

源自38位全网作者

10-19 20:35

一个 11 年的 bug 捅穿了"安全 Rust":先别翻桌,看完这三份证据再站队

2026 年 10 月 13 日,rust-lang/rust 仓库收到 issue #164250:一段完全没有 `unsafe` 的 Rust 代码,可以从 2015 年的 1.4.0 一路干净编译到今天刚发布的 1.99.0,运行时却把已经离开作用域的字符串打了出来,ASan 直接报 `stack-use-after-scope`。GitHub

换句话说,“写安全 Rust、不碰 unsafe,就不会碰内存未定义行为”——这门语言最核心的叙事,此刻被官方实锤打了一个洞。截至 10 月 19 日,这个 issue 仍然 open,修复没有落地。

这周中文社区的动作很快。知乎上"在内存安全和并发安全方面,Rust 真的比 C++ 更好吗""C++ 之父为什么说 Rust 等内存安全语言的安全性并不优于 C++“等问题集中冒出 10 月 12—19 日的新回答,其中一个问题浏览量已堆到 129 万。 小红书已经有人把这事剪成了"Rust 编译通过内存照样不安全,解读 11 年漏洞”。 B 站有人借着 Rust 2026 年度提案继续开火:“rust 一年整 80 个提案,有多少成果自己心里有数”。知乎小红书哔哩哔哩

国外更大的现场在 Hacker News:同一时间线的帖子拿了 747 分、444 条评论。Hacker News

如果你日常写 Rust、拿"内存安全"做过选型理由,或者正被拉进这轮语言战,你的立场需要重新对一遍证据。下面是把 GitHub 一手 issue(含 13 条评论)、Rust 官方 2026 路线图、版本发布记录和中外社区讨论拼在一起之后的完整图景——先看现场,再看温度。

一、25 分钟定性,80 分钟降级:官方自己的反应链

这件事最有信息量的部分,恰恰是 Rust 团队自己的反应速度:

  • 10 月 13 日 08:45(UTC),theemathas 提交 #164250,标签一路挂上 `I-unsound`、`C-bug`、`A-lifetimes`、`A-variance`、`A-borrow-checker`、`T-types`;

  • 09:10,rustbot 按规则先标记 P-critical;

  • 09:16,theemathas 补充:他的原始复现版本从 1.18.0(2017)就能编译;lcnr 在 09:25 给出一个变体,注明 unsound since 1.4(2015),另有人指出可能关联更早的历史 issue #128225。

  • 09:25,优先级被调整为 P-high。lcnr 随后在 issue 置顶说明里解释了降级逻辑:这类 bug 按性质本就该是 P-critical,“之所以只是 P-high,是因为它已经存在 11 年——11 年都没被撞上的 bug,实际触发难度不可能太低”。GitHub

  • 同一段说明给出了关键触发条件:看起来好触发,但需要一个刻意写坏的(malicious)`Drop` 实现来配合;

  • 13:39,版主以"防止蹭热度灌水"为由预防性锁帖;评论区最终停在 13 条;

  • 10 月 16 日,Rust 1.99.1 发布——注意,这个点版本修的是另外三件事:UEFI 目标缺 `wcslen` 符号、x86_64 上 FFI `bool` 返回值的错误编译、macOS 上 rust-lld 缺 `libLLVM` rpath,和本 bug 无关。GitHub发布页

  • 10 月 19 日今天:issue 依然 open。

时间线里最容易被标题党抽走的两个数字是"11 年"和"6 小时定性降级"。前者说明这不是新版本的回归,而是类型系统里躺了一个版本纪元的检查缺口;后者说明官方既没有装死、也没有把它当火灾。

一个 11 年的 bug 捅穿了

二、这个洞到底怎么来的:一份最小复现,和它的三道边界

issue 里给的最小复现浓缩一下是这样:

```rust
struct Q(Option);

impl Drop for Q {
// drop 里把字段内容当成 &'static 数据塞进全局容器
…
}

fn main() {
let l = String::from(“hello world…”);
let mut q = Q(None);
q.0 = Some(&l); // 短命引用被塞进 'static 约束的类型参数
}
```

问题出在字段赋值这条路径:`Q` 的类型参数被 `T: 'static` 约束,但通过协变 + 统一(unification),一个带短生命周期的 `&l` 可以赋进 `q.0`,借用检查在这里没有把类型参数上的生命周期约束查全;随后 `Drop` 执行时按 `'static` 对待它,数据早就出了作用域。中文专栏作者对成因有一句很干净的概括:问题出在方向,变异(variance)允许你在一个方向放,析构(Drop)允许你在另一个方向取,两边都合规,中间那段就漏了。知乎

一个 11 年的 bug 捅穿了

三道边界必须一起看:

  1. 跨版本但跨不了场景:能稳定复现的最低版本追溯到 1.4.0/1.18.0,但触发需要代码作者主动写出那个"按 `'static` 假设干活"的 `Drop`。正常业务代码不会误撞,撞得上的是"有人专门用你的 crate 后门化 Drop"这种供应链场景。

  2. 它是 soundness 问题,不是普遍 UB 事故:官方定性 I-unsound 的含义是"类型系统允许写出不该被接受的程序",而 lcnr 的原话是它"极可能从未造成过实际的内存不安全"。

  3. 它不是 Rust 独有叙事外的事:社区长期有一份公开的声音清单,比如 dtolnay(serde 作者)那份被广泛传播的"Soundness bugs in Rust libraries: can’t live with 'em, can’t live without 'em"——soundness bug 是 Rust 生态公开承认、持续清理的一种债务类别,而不是这次才被爆出来的孤例。docs.rs文档

一个 11 年的 bug 捅穿了

三、AI 测温仪:同一件事,三种站法,哪句有证据

这轮讨论里流传的三句话,逐句对照证据:

流传的说法

一手证据

判定

“Rust 内存安全是假的,信仰塌了”

官方置顶说明:需要恶意 Drop 配合、11 年无人撞见;Project Zero 原文承认这类缺口"obscure and have rarely if ever affected production systems"

夸大:洞是真的,"塌了"不是

“安全 Rust 100% 不会有 UB”

#164250 官方挂 I-unsound 且至今 open;1.99.0、1.99.1、nightly 1.101 均可编译复现

已被官方实锤证伪,这话不能再拿去做选型理由

“这是 C++ 阵营的黑料/节奏”

bug 由 Rust 贡献者自己提交,25 分钟内走完定级,Types Team 成员下场给复现变体;lcnr 明说"按性质就该是 P-critical"

不成立:恰恰是 Rust 团队自曝并推进修复

测温的意义不在和稀泥:三句话里,第二句是错的、第三句是错的、第一句夸大了后果——但"安全 Rust 的保证是有工程代价、有已知缺口的"这个判断本身,是这轮讨论里唯一值得带走的认知。官方在置顶说明里其实已经把触发条件写死了:想利用它,得靠一个刻意写坏的 `Drop` 实现,它极可能从未在现实中造成过内存不安全。GitHub

一个 11 年的 bug 捅穿了

四、官方没有藏着:这个洞在 2026 年还债日程表上

如果你只打算收藏一个链接,收这个:Rust 2026 官方路线图里的 Project Zero 目标页,负责人一栏就是 lcnr、Boxy、Nadrieril、Niko Matsakis——正是 #164250 评论区下场的同一批人。目标原文写得很直白:“修复所有已知的类型系统 soundness bug,让 unsafe 成为产生 UB 的唯一途径”。Rust项目目标

路线图里还引了一个值得放进任何语言战当谈资的数:微软等机构的研究长期显示,约 70% 的安全漏洞源自内存安全问题(Heartbleed、Stuxnet 都算这类)。 Rust 模式的本质是把这 70% 的防守从"靠程序员自觉+事后审计"换成"靠编译器写不出来";这次的洞,是"写不出来"这句话本身的一个例外分支,官方现在正在把这个例外逐个归零。Rust项目目标

同页还挂着两个配套工程目标:next-generation trait solver 和 region constraint rework——生命周期检查缺口属于后者要重建的部分。顺带一提,B 站那句"rust 一年整 80 个提案,有多少成果自己心里有数"的吐槽,和"官方把 faith 级 bug 列进年度还债清单",是同一件事的两面:目标定得激进,兑现进度才是要盯的东西。Project Zero 的完成状态,就是这个问题给不给得起"修完了"三个字的唯一答案。

五、不同身份,不同动作

  • 拿 Rust 做过/准备做选型的技术负责人:不建议用这条 bug 推翻选型结论——它对抗的是 C/C++ 的"人肉防御",这个比较级没有因为 #164250 改变;但汇报和文档里的措辞应该从"Rust 绝对内存安全"改成"无 unsafe 代码的内存安全由编译器强制,已知 soundness 缺口在官方公开追踪修复中"。后者经得起任何一个拿 issue 号来抬杠的人检验。

  • 日常写 Rust 的开发者:你的风险敞口不在这个 bug 本身,而在"依赖的 crate 里有没有恶意或被操纵的 `Drop` 实现"。这条路径值得你重新审一遍新增依赖的来源纪律——它本质上是一条给 Drop 滥用预留的通路,供应链视角比编译器视角更值得紧张。

  • 语言战参与者:下次抛出"Rust 也有 UB"之前,把两边证据一起带上——触发需要恶意 Drop、官方 25 分钟定性、以及它躺在修复清单里;同样,对面抛"70% 漏洞来自内存问题"时,你也知道这句话的出处其实是 Rust 官方路线图,可以直接接话不露怯。

  • 准备学或刚学 Rust 的人:不必因为这个换语言。所有权和借用检查的学习成本没变,但这个事件能让你提前理解一件课本不讲的事:`unsafe` 关键字为什么存在、社区为什么对"soundness"这个词敏感——这是从"教程用户"进阶到"生态用户"的门票。Rust 经常被称为"内存安全语言",但有个重要前提:代码必须遵守 Rust 的安全规则;一旦进入 unsafe 世界,责任就有一部分交还给程序员。Tony Bai博客

  • 继续观察的信号:① #164250 出现修复 PR 并合入哪个版本(留意后续 1.100/1.101 的点发布,参考这次 1.99.1 修的都是别的事);② Project Zero 路线图页的进度更新和 region constraint rework 的落地;③ issue 重新解锁后 Types Team 的复盘口径。

六、最后说句实话

这个存在了 11 年的 bug,给 Rust 的意义其实不是"翻车",而是一次成本很低的成人礼:内存安全从来不是一条打开就关不掉的开关,而是一条有账单的趋势线——编译器拦掉了绝大多数事故,官方把剩下的洞列成公开清单排期还债。拿这条 issue 说"Rust 不过如此"的人,和说"Rust 绝对安全"的人,读的是同一行字,错的也是同一行。

对真正的 Rust 用户,这周只需要带走三件事:选型话术换掉、依赖 Drop 实现过一眼、把 Project Zero 的页面收进收藏夹等它变绿。等 #164250 的修复落地那天,这一轮语言战的所有素材,就都自动过期了。

内容由AI生成
0
扫一下,分享更方便,购买更轻松
0评论

当前文章无评论,是时候发表评论了
提示信息

取消
确认
评论举报

最新文章 热门文章