Skip to content

Commit 9752d3d

Browse files
committed
Allow dereferencing a rooted Gc during collection
Ordinarily, objects dropped during collections would only contain unrooted Gc pointers. But with `#[unsafe_ignore_trace]` it’s possible to smuggle a rooted Gc pointer into the heap. When we collect a rooted Gc pointer, we need to dereference it in order to unroot its contents. This should be safe because the associated allocation cannot have been collected while the Gc is rooted. Fixes an “assertion failed: finalizer_safe()” panic in the included test case (refs Manishearth#52). Signed-off-by: Anders Kaseorg <[email protected]>
1 parent de3769d commit 9752d3d

File tree

2 files changed

+14
-2
lines changed

2 files changed

+14
-2
lines changed

gc/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,12 @@ impl<T: ?Sized> Gc<T> {
141141
#[inline]
142142
fn inner_ptr(&self) -> *mut GcBox<T> {
143143
// If we are currently in the dropping phase of garbage collection,
144-
// it would be undefined behavior to dereference this pointer.
144+
// it would be undefined behavior to dereference an unrooted Gc.
145145
// By opting into `Trace` you agree to not dereference this pointer
146146
// within your drop method, meaning that it should be safe.
147147
//
148148
// This assert exists just in case.
149-
assert!(finalizer_safe());
149+
assert!(finalizer_safe() || self.rooted());
150150

151151
unsafe { clear_root_bit(self.ptr_root.get()).as_ptr() }
152152
}

gc/tests/ignore_trace.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use gc::{force_collect, Finalize, Gc, Trace};
2+
3+
#[derive(Finalize, Trace)]
4+
struct S(#[unsafe_ignore_trace] Gc<()>);
5+
6+
/// Using `#[unsafe_ignore_trace]` on a `Gc` may inhibit collection of
7+
/// cycles through that `Gc`, but it should not result in panics.
8+
#[test]
9+
fn ignore_trace_gc() {
10+
Gc::new(S(Gc::new(())));
11+
force_collect();
12+
}

0 commit comments

Comments
 (0)