5.4 Miri
原文链接: https://rust-unofficial.github.io/too-many-lists/fifth-miri.html
紧张地笑 这些 unsafe 东西太容易了,我不知道为什么大家都说不然。我们的程序完美运行。
旁白: 🙂
……对吧?
旁白: 🙂
嗯,我们现在在写 unsafe 代码,所以编译器不能像以前那样帮我们抓错。测试碰巧能通过,但实际上可能在做非确定性的事。某种未定义行为的事。
但我们能怎么办?我们撬开窗户,溜出了 rustc 的教室。没人能帮我们了。
……等等,巷子里那个鬼鬼祟祟的人是谁?
“嘿小子,想解释一些 Rust 代码吗?”
什——不?为什么,
“太狂野了兄弟,它能验证程序的实际动态执行是否符合 Rust 内存模型的语义。震撼你的心灵……”
什么?
“它检查你是否做了未定义行为。”
我想我可以试一次解释器。
“你装了 rustup 对吧?”
当然装了,这是保持 Rust 工具链最新的那个工具!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| > rustup +nightly-2022-01-21 component add miri
info: syncing channel updates for 'nightly-2022-01-21-x86_64-pc-windows-msvc'
info: latest update on 2022-01-21, rust version 1.60.0-nightly (777bb86bc 2022-01-20)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'rustfmt'
info: downloading component 'miri'
info: installing component 'miri'
|
你刚在我电脑上装了什么!?
“好东西”
旁白: 工具链版本有点奇怪:
我们安装的工具 miri 与 rustc 内部紧密配合,所以只在 nightly 工具链上可用。
+nightly-2022-01-21 告诉 rustup 我们要用那个日期的 rust nightly 工具链安装 miri。我指定具体日期是因为有时 miri 会落后,几个 nightly 都编不过。rustup 会用 + 指定的工具链自动下载我们还没有的。
2022-01-21 只是我知道支持 miri 的一个 nightly,可以在这个状态页查看。如果你运气好可以直接用 +nightly。
每当我们通过 cargo miri 调用 miri 时,也会用这个 + 语法指定安装 miri 的工具链。如果不想每次都指定,可以用 rustup override set。
1
2
3
4
| > cargo +nightly-2022-01-21 miri test
I will run `"cargo.exe" "install" "xargo"` to install
a recent enough xargo. Proceed? [Y/n]
|
呃 XARGO 到底是什么?
“没事,别担心。”
1
2
3
4
5
6
7
8
9
10
11
12
| > y
Updating crates.io index
Installing xargo v0.3.24
...
Finished release [optimized] target(s) in 10.65s
Installing C:\Users\ninte\.cargo\bin\xargo-check.exe
Installing C:\Users\ninte\.cargo\bin\xargo.exe
Installed package `xargo v0.3.24` (executables `xargo-check.exe`, `xargo.exe`)
I will run `"rustup" "component" "add" "rust-src"` to install
the `rust-src` component for the selected toolchain. Proceed? [Y/n]
|
呃???
“谁不喜欢有一份 Rust 源代码的副本?”
1
2
3
4
| > y
info: downloading component 'rust-src'
info: installing component 'rust-src'
|
“噢耶准备好了,精彩部分来了。”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| Compiling lists v0.1.0 (C:\Users\ninte\dev\tmp\lists)
Finished test [unoptimized + debuginfo] target(s) in 0.25s
Running unittests (lists-5cc11d9ee5c3e924.exe)
error: Undefined Behavior: trying to reborrow for Unique at alloc84055,
but parent tag <209678> does not have an appropriate item in
the borrow stack
--> \lib\rustlib\src\rust\library\core\src\option.rs:846:18
|
846 | Some(x) => Some(f(x)),
| ^ trying to reborrow for Unique at alloc84055,
| but parent tag <209678> does not have an
| appropriate item in the borrow stack
|
= help: this indicates a potential bug in the program:
it performed an invalid operation, but the rules it
violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md
for further information
= note: inside `std::option::Option::<std::boxed::Box<fifth::Node<i32>>>::map::<i32, [closure@src\fifth.rs:31:30: 40:10]>` at \lib\rustlib\src\rust\library\core\src\option.rs:846:18
note: inside `fifth::List::<i32>::pop` at src\fifth.rs:31:9
--> src\fifth.rs:31:9
|
31 | / self.head.take().map(|head| {
32 | | let head = *head;
33 | | self.head = head.next;
34 | |
... |
39 | | head.elem
40 | | })
| |__________^
note: inside `fifth::test::basics` at src\fifth.rs:74:20
--> src\fifth.rs:74:20
|
74 | assert_eq!(list.pop(), Some(1));
| ^^^^^^^^^^
note: inside closure at src\fifth.rs:62:5
--> src\fifth.rs:62:5
|
61 | #[test]
| ------- in this procedural macro expansion
62 | / fn basics() {
63 | | let mut list = List::new();
64 | |
65 | | // Check empty list behaves right
... |
96 | | assert_eq!(list.pop(), None);
97 | | }
| |_____^
...
error: aborting due to previous error
|
哇。好厉害的一个错误。
“是啊,看那玩意儿。你爱看。”
谢谢?
“这瓶雌二醇也拿着,你以后会需要的。”
等等为什么?
“你马上要思考内存模型了,相信我。”
旁白: 神秘人随后变成狐狸从墙洞溜走了。作者盯着虚空好几分钟,试图消化刚刚发生的一切。
巷子里的神秘狐狸对我性别的判断是对的:miri 真的是好东西。
那么 miri 是什么?
An experimental interpreter for Rust’s mid-level intermediate representation (MIR). It can run binaries and test suites of cargo projects and detect certain classes of undefined behavior, for example:
- Out-of-bounds memory accesses and use-after-free
- Invalid use of uninitialized data
- Violation of intrinsic preconditions (an unreachable_unchecked being reached, calling copy_nonoverlapping with overlapping ranges, …)
- Not sufficiently aligned memory accesses and references
- Violation of some basic type invariants (a bool that is not 0 or 1, for example, or an invalid enum discriminant)
- Experimental: Violations of the Stacked Borrows rules governing aliasing for reference types
- Experimental: Data races (but no weak memory effects)
On top of that, Miri will also tell you about memory leaks: when there is memory still allocated at the end of the execution, and that memory is not reachable from a global static, Miri will raise an error.
…
However, be aware that Miri will not catch all cases of undefined behavior in your program, and cannot run all programs
TL;DR:它解释你的程序,注意你是否在运行时打破了规则并做了未定义行为。这是必要的,因为未定义行为通常发生在运行时。如果问题能在编译期发现,编译器会直接报错!
如果你熟悉 ubsan 和 tsan:基本上就是那些但合在一起且更极端。
Miri 现在拿着刀趴在教室窗外。一把学习用的刀。
如果我们想让 miri 检查我们的工作,可以请他们这样解释我们的测试套件:
1
| > cargo +nightly-2022-01-21 miri test
|
现在仔细看看他们在我们课桌上刻了什么:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| error: Undefined Behavior: trying to reborrow for Unique at alloc84055, but parent tag <209678> does not have an appropriate item in the borrow stack
--> \lib\rustlib\src\rust\library\core\src\option.rs:846:18
|
846 | Some(x) => Some(f(x)),
| ^ trying to reborrow for Unique at alloc84055,
| but parent tag <209678> does not have an
| appropriate item in the borrow stack
|
= help: this indicates a potential bug in the program: it
performed an invalid operation, but the rules it
violated are still experimental
= help: see
https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md
for further information
|
嗯,看得出我们犯了错,但错误信息很困惑。“borrow stack"是什么?
我们下一节试着搞清楚。