Skip to content

Commit 6c5f754

Browse files
authored
Merge pull request Manishearth#147 from andersk/provenance
Pointer provenance fixes
2 parents 3edb400 + 3586fb6 commit 6c5f754

File tree

2 files changed

+37
-22
lines changed

2 files changed

+37
-22
lines changed

gc/src/gc.rs

+23-17
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::ptr::{self, NonNull};
66
struct GcState {
77
stats: GcStats,
88
config: GcConfig,
9-
boxes_start: Option<NonNull<GcBox<dyn Trace>>>,
9+
boxes_start: Cell<Option<NonNull<GcBox<dyn Trace>>>>,
1010
}
1111

1212
impl Drop for GcState {
@@ -42,7 +42,7 @@ pub fn finalizer_safe() -> bool {
4242
thread_local!(static GC_STATE: RefCell<GcState> = RefCell::new(GcState {
4343
stats: Default::default(),
4444
config: Default::default(),
45-
boxes_start: None,
45+
boxes_start: Cell::new(None),
4646
}));
4747

4848
const MARK_MASK: usize = 1 << (usize::BITS - 1);
@@ -51,15 +51,15 @@ const ROOTS_MAX: usize = ROOTS_MASK; // max allowed value of roots
5151

5252
pub(crate) struct GcBoxHeader {
5353
roots: Cell<usize>, // high bit is used as mark flag
54-
next: Option<NonNull<GcBox<dyn Trace>>>,
54+
next: Cell<Option<NonNull<GcBox<dyn Trace>>>>,
5555
}
5656

5757
impl GcBoxHeader {
5858
#[inline]
5959
pub fn new(next: Option<NonNull<GcBox<dyn Trace>>>) -> Self {
6060
GcBoxHeader {
6161
roots: Cell::new(1), // unmarked and roots count = 1
62-
next,
62+
next: Cell::new(next),
6363
}
6464
}
6565

@@ -137,7 +137,8 @@ impl<T: Trace> GcBox<T> {
137137
data: value,
138138
}));
139139

140-
st.boxes_start = Some(unsafe { NonNull::new_unchecked(gcbox) });
140+
st.boxes_start
141+
.set(Some(unsafe { NonNull::new_unchecked(gcbox) }));
141142

142143
// We allocated some bytes! Let's record it
143144
st.stats.bytes_allocated += mem::size_of::<GcBox<T>>();
@@ -176,6 +177,11 @@ impl<T: Trace + ?Sized> GcBox<T> {
176177
self.header.dec_roots();
177178
}
178179

180+
/// Returns a pointer to the `GcBox`'s value, without dereferencing it.
181+
pub(crate) fn value_ptr(this: *const GcBox<T>) -> *const T {
182+
unsafe { ptr::addr_of!((*this).data) }
183+
}
184+
179185
/// Returns a reference to the `GcBox`'s value.
180186
pub(crate) fn value(&self) -> &T {
181187
&self.data
@@ -186,26 +192,26 @@ impl<T: Trace + ?Sized> GcBox<T> {
186192
fn collect_garbage(st: &mut GcState) {
187193
st.stats.collections_performed += 1;
188194

189-
struct Unmarked {
190-
incoming: *mut Option<NonNull<GcBox<dyn Trace>>>,
195+
struct Unmarked<'a> {
196+
incoming: &'a Cell<Option<NonNull<GcBox<dyn Trace>>>>,
191197
this: NonNull<GcBox<dyn Trace>>,
192198
}
193-
unsafe fn mark(head: &mut Option<NonNull<GcBox<dyn Trace>>>) -> Vec<Unmarked> {
199+
unsafe fn mark(head: &Cell<Option<NonNull<GcBox<dyn Trace>>>>) -> Vec<Unmarked<'_>> {
194200
// Walk the tree, tracing and marking the nodes
195-
let mut mark_head = *head;
201+
let mut mark_head = head.get();
196202
while let Some(node) = mark_head {
197203
if (*node.as_ptr()).header.roots() > 0 {
198204
(*node.as_ptr()).trace_inner();
199205
}
200206

201-
mark_head = (*node.as_ptr()).header.next;
207+
mark_head = (*node.as_ptr()).header.next.get();
202208
}
203209

204210
// Collect a vector of all of the nodes which were not marked,
205211
// and unmark the ones which were.
206212
let mut unmarked = Vec::new();
207213
let mut unmark_head = head;
208-
while let Some(node) = *unmark_head {
214+
while let Some(node) = unmark_head.get() {
209215
if (*node.as_ptr()).header.is_marked() {
210216
(*node.as_ptr()).header.unmark();
211217
} else {
@@ -214,33 +220,33 @@ fn collect_garbage(st: &mut GcState) {
214220
this: node,
215221
});
216222
}
217-
unmark_head = &mut (*node.as_ptr()).header.next;
223+
unmark_head = &(*node.as_ptr()).header.next;
218224
}
219225
unmarked
220226
}
221227

222-
unsafe fn sweep(finalized: Vec<Unmarked>, bytes_allocated: &mut usize) {
228+
unsafe fn sweep(finalized: Vec<Unmarked<'_>>, bytes_allocated: &mut usize) {
223229
let _guard = DropGuard::new();
224230
for node in finalized.into_iter().rev() {
225231
if (*node.this.as_ptr()).header.is_marked() {
226232
continue;
227233
}
228234
let incoming = node.incoming;
229-
let mut node = Box::from_raw(node.this.as_ptr());
235+
let node = Box::from_raw(node.this.as_ptr());
230236
*bytes_allocated -= mem::size_of_val::<GcBox<_>>(&*node);
231-
*incoming = node.header.next.take();
237+
incoming.set(node.header.next.take());
232238
}
233239
}
234240

235241
unsafe {
236-
let unmarked = mark(&mut st.boxes_start);
242+
let unmarked = mark(&st.boxes_start);
237243
if unmarked.is_empty() {
238244
return;
239245
}
240246
for node in &unmarked {
241247
Trace::finalize_glue(&(*node.this.as_ptr()).data);
242248
}
243-
mark(&mut st.boxes_start);
249+
mark(&st.boxes_start);
244250
sweep(unmarked, &mut st.stats.bytes_allocated);
245251
}
246252
}

gc/src/lib.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ impl<T: Trace + ?Sized> Gc<T> {
101101
/// Returns the given pointer with its root bit cleared.
102102
unsafe fn clear_root_bit<T: ?Sized + Trace>(ptr: NonNull<GcBox<T>>) -> NonNull<GcBox<T>> {
103103
let ptr = ptr.as_ptr();
104-
let ptr = set_data_ptr(ptr, (ptr as *mut u8 as usize & !1) as *mut u8);
104+
let data = ptr as *mut u8;
105+
let addr = data as isize;
106+
let ptr = set_data_ptr(ptr, data.wrapping_offset((addr & !1) - addr));
105107
NonNull::new_unchecked(ptr)
106108
}
107109

@@ -112,7 +114,9 @@ impl<T: Trace + ?Sized> Gc<T> {
112114

113115
unsafe fn set_root(&self) {
114116
let ptr = self.ptr_root.get().as_ptr();
115-
let ptr = set_data_ptr(ptr, (ptr as *mut u8 as usize | 1) as *mut u8);
117+
let data = ptr as *mut u8;
118+
let addr = data as isize;
119+
let ptr = set_data_ptr(ptr, data.wrapping_offset((addr | 1) - addr));
116120
self.ptr_root.set(NonNull::new_unchecked(ptr));
117121
}
118122

@@ -121,7 +125,7 @@ impl<T: Trace + ?Sized> Gc<T> {
121125
}
122126

123127
#[inline]
124-
fn inner(&self) -> &GcBox<T> {
128+
fn inner_ptr(&self) -> *mut GcBox<T> {
125129
// If we are currently in the dropping phase of garbage collection,
126130
// it would be undefined behavior to dereference this pointer.
127131
// By opting into `Trace` you agree to not dereference this pointer
@@ -130,7 +134,12 @@ impl<T: Trace + ?Sized> Gc<T> {
130134
// This assert exists just in case.
131135
assert!(finalizer_safe());
132136

133-
unsafe { &*clear_root_bit(self.ptr_root.get()).as_ptr() }
137+
unsafe { clear_root_bit(self.ptr_root.get()).as_ptr() }
138+
}
139+
140+
#[inline]
141+
fn inner(&self) -> &GcBox<T> {
142+
unsafe { &*self.inner_ptr() }
134143
}
135144
}
136145

@@ -152,7 +161,7 @@ impl<T: Trace + ?Sized> Gc<T> {
152161
/// assert_eq!(unsafe { *x_ptr }, 22);
153162
/// ```
154163
pub fn into_raw(this: Self) -> *const T {
155-
let ptr: *const T = &*this;
164+
let ptr: *const T = GcBox::value_ptr(this.inner_ptr());
156165
mem::forget(this);
157166
ptr
158167
}

0 commit comments

Comments
 (0)