Dead objects resurrected by finalizer are incorrectly deallocated
andersk opened this issue · comments
Anders Kaseorg commented
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:
Lines 198 to 200 in e459223
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}