Hacker News 上的一份报告描述了 Rust 中的一个健全性漏洞:通过协变字段赋值,结构体的生命周期要求可以被绕过。报告者分享了一个小程序,其中使用了一个包装类型 Q<T>,它持有一个 Option<T>,并实现了 Drop。Drop 实现会将所包含的值移入一个静态的 Mutex<Vec<Box<dyn Any + Send>>> 中,而对于不具有 'static 生命周期的借用值来说,这本应是不可能的。
在示例中,一个局部 String 在内部作用域中通过引用被赋值给该包装器,因此借用本应在该作用域结束时终止。由于包装器的字段是协变的,编译器接受了这一赋值,于是该引用可以被存储到局部变量的生命周期之外。该程序能够通过编译,并打印出无关的内存内容,而不是被编译器拒绝。
在 AddressSanitizer 下运行时,程序会因 stack-use-after-scope 错误而崩溃,这证实了悬垂引用确实被解引用了。据报告,该问题在当前的 nightly 版本以及至少可追溯到 1.85.0 的稳定版本中都能复现。帖子中附有带和不带 sanitizer 的 Compiler Explorer 示例链接。
这一发现表明,在变型与基于 Drop 的存储相互作用的方式上存在健全性漏洞。Rust 维护者通常将此类问题视为高优先级,因为安全代码永远不应产生未定义行为。
常见问题:为什么这个 Rust 程序尽管存在悬垂引用仍能通过编译?
由于字段的协变性,编译器接受了对生命周期较短的引用的赋值,随后 Drop 实现将该引用存入一个 'static 容器中,使其使用范围超出了局部变量的生命周期。这是一个健全性漏洞,编译器本应拒绝此代码。
来源: Hacker News · 由HeadlinesBriefing整理摘要