Rustコード の 未定義挙動の検知ツール Miri を試してみる。
こんにちは k-jun です。今回は Rust の 定義されていない挙動、メモリリーク、バグなどを発見する Miri を試してみます。
https://github.com/rust-lang/miri
具体的には以下のような挙動を発見できるようです。
- 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)
Miri will also tell you about memory leaks
- Miri has already discovered some real-world bugs
実際に使ってみて、どのようなものかを見てみます。まずは Install から。
Install
$ rustup +nightly component add miri info: downloading component 'miri' info: installing component 'miri'
以前 小さい Redis のようなものを Rust で作成したことがあるので、Miri を実行して見ます。
https://github.com/k-jun/poinsettia
$ cargo +nightly miri run --bin server Compiling poinsettia v0.1.0 (/Users/k-jun/ghq/github.com/k-jun/poinsettia) Finished dev [unoptimized + debuginfo] target(s) in 0.05s Running `/Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/cargo-miri target/miri/x86_64-apple-darwin/debug/server` error: unsupported operation: can't call foreign function: kqueue --> /Users/k-jun/.cargo/registry/src/github.com-1ecc6299db9ec823/mio-0.7.6/src/sys/unix/selector/kqueue.rs:78:9 | 78 | syscall!(kqueue()) | ^^^^^^^^^^^^^^^^^^ can't call foreign function: kqueue | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: inside `mio::sys::unix::selector::kqueue::Selector::new` at /Users/k-jun/.cargo/registry/src/github.com-1ecc6299db9ec823/mio-0.7.6/src/sys/unix/mod.rs:7:28 = note: inside `mio::poll::Poll::new` at /Users/k-jun/.cargo/registry/src/github.com-1ecc6299db9ec823/mio-0.7.6/src/poll.rs:354:13 = note: inside `tokio::io::driver::Driver::new` at /Users/k-jun/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.3.4/src/io/driver/mod.rs:115:20 = note: inside `tokio::runtime::driver::create_io_stack` at /Users/k-jun/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.3.4/src/runtime/driver.rs:22:29 = note: inside `tokio::runtime::driver::Driver::new` at /Users/k-jun/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.3.4/src/runtime/driver.rs:168:52 = note: inside `tokio::runtime::Builder::build_threaded_runtime` at /Users/k-jun/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.3.4/src/runtime/builder.rs:498:39 = note: inside `tokio::runtime::Builder::build` at /Users/k-jun/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.3.4/src/runtime/builder.rs:376:34 note: inside `main` at src/bin/server.rs:5:1 --> src/bin/server.rs:5:1 | 5 | #[tokio::main] | ^^^^^^^^^^^^^^ = note: inside `<fn() -> std::result::Result<(), std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>> as std::ops::FnOnce<()>>::call_once - shim(fn() -> std::result::Result<(), std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>)` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5 = note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn() -> std::result::Result<(), std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>, std::result::Result<(), std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:125:18 = note: inside closure at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:63:18 = note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:259:13 = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/panicking.rs:403:40 = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/panicking.rs:367:19 = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/panic.rs:129:14 = note: inside closure at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:45:48 = note: inside `std::panicking::r#try::do_call::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/panicking.rs:403:40 = note: inside `std::panicking::r#try::<isize, [closure@std::rt::lang_start_internal::{closure#2}]>` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/panicking.rs:367:19 = note: inside `std::panic::catch_unwind::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/panic.rs:129:14 = note: inside `std::rt::lang_start_internal` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:45:20 = note: inside `std::rt::lang_start::<std::result::Result<(), std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>` at /Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:62:5 = note: this error originates in the macro `syscall` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error
なんかよーわからんものがいっぱい出てきましたね...。 "can't call foreign function: kqueue" に関しては調べてみると以下の issue がヒット。
https://github.com/rust-lang/miri/issues/641
どうも Miri から外部の関数が呼び出せないのが問題なような。
"this error originates in the macro syscall
" こちらに関しても、調べた限りあまり有効な指摘ではないようです。残念。
適当なコードに対して実行してみると、
$ cargo +nightly miri run Finished dev [unoptimized + debuginfo] target(s) in 0.01s Running `/Users/k-jun/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/cargo-miri target/miri/x86_64-apple-darwin/debug/playground-rust` warning: static variable `state` should have an upper case name --> src/main.rs:5:8 | 5 | static state: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new())); | ^^^^^ help: convert the identifier to upper case: `STATE` | = note: `#[warn(non_upper_case_globals)]` on by default
state を大文字にしろとかわりかしまともなことを教えてくれます。
全体としてこれを使いこなすには自分の Rust 力がまだまだ力不足なんでしょうね...。精進します。 それでは今日はこのへんで。