Rust 团队很高兴地宣布 Rust 的最新版本 1.15.1。Rust 是一种专注于安全性、速度和并发性的系统编程语言。
如果您已安装了先前版本的 Rust,则获取 Rust 1.15.1 非常简单,只需执行以下命令:
$ rustup update stable
如果您尚未安装,您可以从我们网站的相应页面下载 Rust,并查看 GitHub 上的 1.15.1 的详细发布说明。
1.15.1 稳定版中的内容
此版本修复了两个问题,一个是新的 vec::IntoIter::as_mut_slice
方法中的健全性错误,另一个是 Rust 发行版的某些 C 组件 没有使用 -fPIC
编译而导致的回归。后者会导致可执行文件的文本段在某些配置(包括常见的 Linux 配置)中可写,从而破坏重要的攻击缓解措施,并通过导致链接器执行更多工作来延长启动时间。对于主要由 Rust 编写的代码库来说,丢失只读文本段的实际影响相对较小(因为 Rust 的类型系统是它的第一道防线),但对于链接到其他代码库的 Rust 来说,影响可能会出乎意料地非常显著。PIC 问题是众所周知的,并非 Rust 特有的,因此本文的其余部分重点讨论健全性错误。
as_mut_slice
的问题(一个三行函数)是在发布 Rust 1.15.0 几分钟后被发现的,它提醒我们编写不安全代码的风险。
as_mut_slice
是 Vec
类型的 IntoIter
迭代器上的一个方法,它提供了对正在迭代的缓冲区的可变视图。从概念上讲,它很简单:只需返回对缓冲区的引用;实际上,实现也很简单,但是它是不安全的,因为 IntoIter
是使用指向缓冲区的不安全指针实现的。
pub fn as_mut_slice(&self) -> &mut [T] {
unsafe {
slice::from_raw_parts_mut(self.ptr as *mut T, self.len())
}
}
这几乎是最简单的不安全方法之一。你能发现错误吗?我们的审查员没有发现!这个 API 溜掉了,因为它非常标准且小巧。这是一个审查员忽略的复制粘贴错误。此方法采用共享引用,并从中不安全地派生出可变引用。这是完全错误的,因为它意味着可以使用 as_mut_slice
来生成对同一缓冲区的多个可变引用,这正是您在 Rust 中绝对不能做的事情。
这是一个简单的示例,说明此错误会导致您错误地编写什么:
fn main() {
let v = vec![0];
let v_iter = v.into_iter();
let slice1: &mut [_] = v_iter.as_mut_slice();
let slice2: &mut [_] = v_iter.as_mut_slice();
slice1[0] = 1;
slice2[0] = 2;
}
这里 slice1
和 slice2
都引用同一个可变切片。还要注意,它们创建的迭代器 v_iter
没有声明为可变的,这很好地表明有可疑的事情正在发生。
这里的解决方案很简单,只需将 &self
更改为 &mut self
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe {
slice::from_raw_parts_mut(self.ptr as *mut T, self.len())
}
}
这样,就维护了正确的唯一性不变性,一次只能创建一个可变切片,并且必须将 v_iter
声明为可变的才能拉出可变切片。
因此,我们进行了更改并发布了修复程序。在 Rust 中,我们以不破坏 API 为荣,但是由于这是一个新的次要功能,并且当前的实现非常不健全,因此我们决定立即发布修复程序,希望在太多代码库使用它之前——也就是说,我们不认为这是一个需要仔细过渡的破坏性更改,而是一个必要的错误修复。有关 Rust 确保稳定性的方法的更多信息,请参阅 “稳定性作为可交付成果” 博客文章、关于语言演变的 RFC 1122 和关于库演变的 RFC 1105。
我们仍在评估从中吸取什么教训,但这很好地提醒了我们在编写不安全代码时必须谨慎。我们有一些关于如何在开发过程的早期捕获此类问题的想法,但尚未做出任何决定。
对于由此带来的不便,我们深感抱歉。让我们开始黑客吧。
1.15.1 的贡献者
有 2 位个人为 Rust 1.15.1 贡献了代码。 感谢!