error: memory leaked: alloc217 (Rust heap, size: 40, align: 8), allocated here:

Miri がメモリリークを検出した場合のエラー。

検出方法

確保したメモリがプログラム完了時点で未解放、かつグローバルな static 領域からそれらへとポインタを辿って到達不可能な場合、メモリリークと判定される。

パターン

パターン A

unsafe の誤用

unsafe の誤用によりメモリ管理が破綻するパターン。

サンプル

以下では、unsafe な機能で Rc の参照カウンタを強引に操作している。


use std::rc::Rc;

fn main() {
    let rc = Rc::new(42);
    let ptr = Rc::into_raw(rc);
    unsafe {
        Rc::increment_strong_count(ptr);
    }
}

error: memory leaked: alloc207 (Rust heap, size: 24, align: 8), allocated here:
   --> C:\Users\nossi\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\rc.rs:412:27
    |
412 |                 Box::leak(Box::new(RcInner { strong: Cell::new(1), weak: Cell::new(1), value }))
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: BACKTRACE:
    = note: inside `std::rc::Rc::<i32>::new` at C:\Users\nossi\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\rc.rs:412:27: 412:96
note: inside `main`
   --> src\main.rs:4:14
    |
  4 |     let rc = Rc::new(42);
    |              ^^^^^^^^^^^

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

note: set `MIRIFLAGS=-Zmiri-ignore-leaks` to disable this check

パターン B

循環参照

参照カウンタ方式の型 (RcArc など) が循環参照して解放されないパターン。

サンプル

以下では、単方向リンクの末尾が先頭と接続され、Rc が循環参照している。


use std::rc::Rc;
use std::cell::RefCell;

fn main() {
    // Construct links.
    let n1 = &mut LinkNode::new(1);
    let n2 = &mut LinkNode::new(2);
    n1.borrow_mut().set_next(n2);
    n2.borrow_mut().set_next(n1);

    // Assert links.
    let n1_next = n1.borrow().next();
    assert_eq!(n1_next.borrow().value, n2.borrow().value);
}

struct LinkNode {
    value: i32,
    next: Option<Rc<RefCell<LinkNode>>>,
}

impl LinkNode {
    pub fn new(value: i32) -> Rc<RefCell<LinkNode>> {
        let next = None;
        Rc::new(RefCell::new(Self { value, next }))
    }

    pub fn next(&self) -> Rc<RefCell<LinkNode>> {
        self.next.as_ref().unwrap().clone()
    }

    pub fn set_next(&mut self, node: &Rc<RefCell<LinkNode>>) {
        self.next = Some(node.clone());
    }
}

error: memory leaked: alloc217 (Rust heap, size: 40, align: 8), allocated here:
   --> C:\Users\nossi\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\rc.rs:412:27
    |
412 |                 Box::leak(Box::new(RcInner { strong: Cell::new(1), weak: Cell::new(1), value }))
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: BACKTRACE:
    = note: inside `std::rc::Rc::<std::cell::RefCell<LinkNode>>::new` at C:\Users\nossi\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\rc.rs:412:27: 412:96
note: inside `LinkNode::new`
   --> src\main.rs:24:9
    |
 24 |         Rc::new(RefCell::new(Self { value, next }))
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `main`
   --> src\main.rs:6:19
    |
  6 |     let n1 = &mut LinkNode::new(1);
    |                   ^^^^^^^^^^^^^^^^

error: memory leaked: alloc285 (Rust heap, size: 40, align: 8), allocated here:
   --> C:\Users\nossi\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\rc.rs:412:27
    |
412 |                 Box::leak(Box::new(RcInner { strong: Cell::new(1), weak: Cell::new(1), value }))
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: BACKTRACE:
    = note: inside `std::rc::Rc::<std::cell::RefCell<LinkNode>>::new` at C:\Users\nossi\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\rc.rs:412:27: 412:96
note: inside `LinkNode::new`
   --> src\main.rs:24:9
    |
 24 |         Rc::new(RefCell::new(Self { value, next }))
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `main`
   --> src\main.rs:7:19
    |
  7 |     let n2 = &mut LinkNode::new(2);
    |                   ^^^^^^^^^^^^^^^^

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

note: set `MIRIFLAGS=-Zmiri-ignore-leaks` to disable this check