Manishearth / rust-gc

Simple tracing (mark and sweep) garbage collector for Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Dead objects resurrected by finalizer are incorrectly deallocated

andersk opened this issue · comments

The program below uses a finalizer to resurrect a dead object by linking it back to a root. force_collect deallocates the object anyway, leading to use after free.

It looks like this check was intended to address this problem:

rust-gc/gc/src/gc.rs

Lines 198 to 200 in e459223

if (*node.this.as_ptr()).header.marked.get() {
continue;
}

Unfortunately, the marked bit is always clear except in the middle of mark, so this check is always false.

Reproduction:

use gc::{force_collect, Finalize, Gc, GcCell, Trace};

#[derive(Finalize, Trace)]
struct Foo {
    bar: GcCell<Option<Gc<Bar>>>,
}

#[derive(Trace)]
struct Bar {
    string: String,
    foo: Gc<Foo>,
    this: GcCell<Option<Gc<Bar>>>,
}

impl Finalize for Bar {
    fn finalize(&self) {
        *self.foo.bar.borrow_mut() = self.this.borrow().clone();
    }
}

fn main() {
    let foo = Gc::new(Foo {
        bar: GcCell::new(None),
    });
    let bar = Gc::new(Bar {
        string: "Hello, world!".to_string(),
        foo: foo.clone(),
        this: GcCell::new(None),
    });
    *bar.this.borrow_mut() = Some(bar.clone());
    drop(bar);
    force_collect();
    println!("{:?}", foo.bar.borrow().as_ref().unwrap().string);
}

This outputs garbage like

"\u{0}-��\u{55000}\u{4}\u{0}\u{0}\u{0}\u{0}"

or

thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '-' (bytes 1..2) of `-��V`', library/core/src/fmt/mod.rs:2064:30
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
"\u{0}