2025 年 11 月,Google 披露了一组让整个安全圈安静下来的数字:
- Android 平台内存安全类漏洞占比,首次跌破 20%——而迁移 Rust 之前,这类漏洞长期是绝对主力。
- Rust 代码的内存安全漏洞密度约 0.2 个/百万行,C/C++ 约 1000 个/百万行,差距约 1000 倍。
- 顺带还发现:Rust 变更的回滚率只有 C++ 的四分之一,代码审查时间少了 25%——更安全的写法,反而更快。
这不是营销话术,是 Android 团队拿 500 万行 Rust 代码和等量 C++ 做跨语言对比得出的结论。本文拆解它为什么成立,以及你能抄什么作业。
整类漏洞,被编译期消灭了
内存安全漏洞的几大主力——悬垂指针(use-after-free)、双重释放(double-free)、缓冲区溢出、数据竞争——本质上都是「访问了不该访问的内存」。Rust 的所有权 + 借用检查 + 生命周期,在编译期就禁止了这些模式:
- 每块内存同一时刻要么有一个可变借用,要么有多个不可变借用,杜绝数据竞争;
- 值被移动或释放后,旧引用无法再使用,杜绝 use-after-free;
- 切片/索引访问在边界内,缓冲区溢出无处藏身。
C/C++ 把这些检查全留给了运行时和你自己的自律;Rust 把它们交给了编译器。
动手对照:use-after-free
同样是「释放后继续使用」,C 和 Rust 的结局完全不同。
C 版(未定义行为,可能被利用):
char *buf = malloc(16);
free(buf);
// 未定义行为:buf 已释放却继续使用
strcpy(buf, "hello"); // 可能覆写任意内存,形成利用原语
编译器一声不吭,程序跑起来可能没事,也可能在某次更新后变成远程代码执行。
Rust 版(编译期直接拒绝):
let mut buf = vec![0u8; 16];
drop(buf);
// buf.push(1); // ❌ 编译错误:borrow of moved value
哪怕你不显式 drop,只要把 buf 的引用往函数外传、或和它的生命周期产生冲突,借用检查器就会在编译期拦下来。没有「运行时才发现」这一步。
再比如返回指向局部缓冲区的引用——C 里经典陷阱,Rust 直接编译失败:
fn build() -> &'static [u8] {
let data = [1u8, 2, 3];
&data // ❌ 编译错误:returns reference to local variable
}
真实案例:Rust 也会出事,但防御更密
Rust 不是银弹。Google 在 CrabbyAVIF(一个用 unsafe Rust 写的 AVIF 图像解析器)里发现过一个线性缓冲区溢出,编号 CVE-2025-48530,CVSS 8.1,理论上可导致远程代码执行。
但它最终没被利用——因为 Android 的 Scudo 强化分配器在缓冲区块周围加了防护页,把这次溢出直接变成了可观测的崩溃,在测试阶段就被拦截。这个「近失」说明两件事:
- unsafe 块仍要按安全代码的标准对待,必须写
// SAFETY:注释、做模糊测试; - 语言安全 + 分配器/ sanitizer 的多层防御,比单靠语言有效得多。
Google 的原话是:unsafe Rust「本身已相当安全」,漏洞密度仍远低于 C/C++,而且「unsafe」并不会自动关闭语言的安全检查。
你可以抄的作业
- 新代码默认 Rust:尤其是解析器、网络协议、系统底层这些高危区。
- 存量高危组件用 Rust 重写:图像(PNG/AVIF)、字体、JSON、压缩等解析器是 CVE 重灾区。Chromium 已把 PNG、JSON、网页字体解析器换成 Rust 实现。
- unsafe 必须配 SAFETY 注释 + 模糊测试:每个 unsafe 块写清指针来源、生命周期、不变量;用
cargo fuzz打一遍。 - FFI 边界单独审计:和 C/C++ 交互的地方是漏洞集中营,做好边界校验和所有权移交。
- 保留深度防御:即使全量 Rust,也别撤掉 Scudo / AddressSanitizer / 模糊测试——它们是最后一道闸。
安全不是「换门语言就高枕无忧」,但 1000 倍的密度差距意味着:把高风险代码搬进 Rust,你是在用编译器的强制力,把整类漏洞从「靠人不出错」变成「编译器不让出错」。这笔账,Google 已经帮你算清楚了。