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 力がまだまだ力不足なんでしょうね...。精進します。 それでは今日はこのへんで。