Rust 1.15.1 发布

2017 年 2 月 9 日 · Rust 核心团队

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_sliceVec 类型的 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;
}

这里 slice1slice2 都引用同一个可变切片。还要注意,它们创建的迭代器 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 贡献了代码。 感谢!