Skip to content

Commit 3b4893b

Browse files
committed
epoch: Fix stacked borrows violations
1 parent e549b69 commit 3b4893b

File tree

4 files changed

+76
-64
lines changed

4 files changed

+76
-64
lines changed

ci/miri.sh

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,24 @@ MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disab
1414
MIRI_LEAK_CHECK='1' \
1515
cargo miri test \
1616
-p crossbeam-channel \
17+
-p crossbeam-epoch \
1718
-p crossbeam-queue \
18-
-p crossbeam-utils 2>&1 | ts -i '%.s '
19+
-p crossbeam-utils \
20+
-p crossbeam 2>&1 | ts -i '%.s '
1921

2022
# -Zmiri-ignore-leaks is needed because we use detached threads in tests in tests/golang.rs: https://github.com/rust-lang/miri/issues/1371
2123
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks" \
2224
cargo miri test \
2325
-p crossbeam-channel --test golang 2>&1 | ts -i '%.s '
2426

25-
# Use Tree Borrows instead of Stacked Borrows because epoch is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/545#issuecomment-1192785003
27+
# Use Tree Borrows instead of Stacked Borrows because skiplist is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/878
2628
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-tree-borrows" \
2729
cargo miri test \
28-
-p crossbeam-epoch \
29-
-p crossbeam-skiplist \
30-
-p crossbeam 2>&1 | ts -i '%.s '
30+
-p crossbeam-skiplist 2>&1 | ts -i '%.s '
3131

32-
# Use Tree Borrows instead of Stacked Borrows because epoch is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/545#issuecomment-1192785003
3332
# -Zmiri-compare-exchange-weak-failure-rate=0.0 is needed because some sequential tests (e.g.,
3433
# doctest of Stealer::steal) incorrectly assume that sequential weak CAS will never fail.
3534
# -Zmiri-preemption-rate=0 is needed because this code technically has UB and Miri catches that.
36-
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-tree-borrows -Zmiri-compare-exchange-weak-failure-rate=0.0 -Zmiri-preemption-rate=0" \
35+
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-compare-exchange-weak-failure-rate=0.0 -Zmiri-preemption-rate=0" \
3736
cargo miri test \
3837
-p crossbeam-deque 2>&1 | ts -i '%.s '

crossbeam-epoch/src/atomic.rs

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use core::marker::PhantomData;
77
use core::mem::{self, MaybeUninit};
88
use core::ops::{Deref, DerefMut};
99
use core::ptr;
10-
use core::slice;
1110

1211
use crate::guard::Guard;
1312
#[cfg(not(miri))]
@@ -125,26 +124,26 @@ pub trait Pointable {
125124
///
126125
/// - The given `ptr` should have been initialized with [`Pointable::init`].
127126
/// - `ptr` should not have yet been dropped by [`Pointable::drop`].
128-
/// - `ptr` should not be mutably dereferenced by [`Pointable::deref_mut`] concurrently.
129-
unsafe fn deref<'a>(ptr: *mut ()) -> &'a Self;
127+
/// - `ptr` should not be mutably dereferenced by [`Pointable::as_mut_ptr`] concurrently.
128+
unsafe fn as_ptr(ptr: *mut ()) -> *const Self;
130129

131130
/// Mutably dereferences the given pointer.
132131
///
133132
/// # Safety
134133
///
135134
/// - The given `ptr` should have been initialized with [`Pointable::init`].
136135
/// - `ptr` should not have yet been dropped by [`Pointable::drop`].
137-
/// - `ptr` should not be dereferenced by [`Pointable::deref`] or [`Pointable::deref_mut`]
136+
/// - `ptr` should not be dereferenced by [`Pointable::as_ptr`] or [`Pointable::as_mut_ptr`]
138137
/// concurrently.
139-
unsafe fn deref_mut<'a>(ptr: *mut ()) -> &'a mut Self;
138+
unsafe fn as_mut_ptr(ptr: *mut ()) -> *mut Self;
140139

141140
/// Drops the object pointed to by the given pointer.
142141
///
143142
/// # Safety
144143
///
145144
/// - The given `ptr` should have been initialized with [`Pointable::init`].
146145
/// - `ptr` should not have yet been dropped by [`Pointable::drop`].
147-
/// - `ptr` should not be dereferenced by [`Pointable::deref`] or [`Pointable::deref_mut`]
146+
/// - `ptr` should not be dereferenced by [`Pointable::as_ptr`] or [`Pointable::as_mut_ptr`]
148147
/// concurrently.
149148
unsafe fn drop(ptr: *mut ());
150149
}
@@ -158,12 +157,12 @@ impl<T> Pointable for T {
158157
Box::into_raw(Box::new(init)).cast::<()>()
159158
}
160159

161-
unsafe fn deref<'a>(ptr: *mut ()) -> &'a Self {
162-
unsafe { &*(ptr as *const T) }
160+
unsafe fn as_ptr(ptr: *mut ()) -> *const Self {
161+
ptr as *const T
163162
}
164163

165-
unsafe fn deref_mut<'a>(ptr: *mut ()) -> &'a mut Self {
166-
unsafe { &mut *ptr.cast::<T>() }
164+
unsafe fn as_mut_ptr(ptr: *mut ()) -> *mut Self {
165+
ptr.cast::<T>()
167166
}
168167

169168
unsafe fn drop(ptr: *mut ()) {
@@ -225,17 +224,22 @@ impl<T> Pointable for [MaybeUninit<T>] {
225224
}
226225
}
227226

228-
unsafe fn deref<'a>(ptr: *mut ()) -> &'a Self {
227+
unsafe fn as_ptr(ptr: *mut ()) -> *const Self {
229228
unsafe {
230-
let array = &*(ptr as *const Array<T>);
231-
slice::from_raw_parts(array.elements.as_ptr(), array.len)
229+
let len = (*ptr.cast::<Array<T>>()).len;
230+
// Use addr_of_mut for stacked borrows: https://github.com/rust-lang/miri/issues/1976
231+
let elements =
232+
ptr::addr_of_mut!((*ptr.cast::<Array<T>>()).elements).cast::<MaybeUninit<T>>();
233+
ptr::slice_from_raw_parts(elements, len)
232234
}
233235
}
234236

235-
unsafe fn deref_mut<'a>(ptr: *mut ()) -> &'a mut Self {
237+
unsafe fn as_mut_ptr(ptr: *mut ()) -> *mut Self {
236238
unsafe {
237-
let array = &mut *ptr.cast::<Array<T>>();
238-
slice::from_raw_parts_mut(array.elements.as_mut_ptr(), array.len)
239+
let len = (*ptr.cast::<Array<T>>()).len;
240+
let elements =
241+
ptr::addr_of_mut!((*ptr.cast::<Array<T>>()).elements).cast::<MaybeUninit<T>>();
242+
ptr::slice_from_raw_parts_mut(elements, len)
239243
}
240244
}
241245

@@ -846,7 +850,7 @@ impl<T: ?Sized + Pointable> fmt::Pointer for Atomic<T> {
846850
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847851
let data = self.data.load(Ordering::SeqCst);
848852
let (raw, _) = decompose_tag::<T>(data);
849-
fmt::Pointer::fmt(&(unsafe { T::deref(raw) as *const _ }), f)
853+
fmt::Pointer::fmt(&(unsafe { T::as_ptr(raw) }), f)
850854
}
851855
}
852856

@@ -1134,14 +1138,14 @@ impl<T: ?Sized + Pointable> Deref for Owned<T> {
11341138

11351139
fn deref(&self) -> &T {
11361140
let (raw, _) = decompose_tag::<T>(self.data);
1137-
unsafe { T::deref(raw) }
1141+
unsafe { &*T::as_ptr(raw) }
11381142
}
11391143
}
11401144

11411145
impl<T: ?Sized + Pointable> DerefMut for Owned<T> {
11421146
fn deref_mut(&mut self) -> &mut T {
11431147
let (raw, _) = decompose_tag::<T>(self.data);
1144-
unsafe { T::deref_mut(raw) }
1148+
unsafe { &mut *T::as_mut_ptr(raw) }
11451149
}
11461150
}
11471151

@@ -1291,6 +1295,18 @@ impl<'g, T: ?Sized + Pointable> Shared<'g, T> {
12911295
raw.is_null()
12921296
}
12931297

1298+
#[doc(hidden)]
1299+
pub unsafe fn as_ptr(&self) -> *const T {
1300+
let (raw, _) = decompose_tag::<T>(self.data);
1301+
unsafe { T::as_ptr(raw) }
1302+
}
1303+
1304+
#[doc(hidden)]
1305+
pub unsafe fn as_mut_ptr(&self) -> *mut T {
1306+
let (raw, _) = decompose_tag::<T>(self.data);
1307+
unsafe { T::as_mut_ptr(raw) }
1308+
}
1309+
12941310
/// Dereferences the pointer.
12951311
///
12961312
/// Returns a reference to the pointee that is valid during the lifetime `'g`.
@@ -1324,8 +1340,7 @@ impl<'g, T: ?Sized + Pointable> Shared<'g, T> {
13241340
/// # unsafe { drop(a.into_owned()); } // avoid leak
13251341
/// ```
13261342
pub unsafe fn deref(&self) -> &'g T {
1327-
let (raw, _) = decompose_tag::<T>(self.data);
1328-
unsafe { T::deref(raw) }
1343+
unsafe { &*self.as_ptr() }
13291344
}
13301345

13311346
/// Dereferences the pointer.
@@ -1366,8 +1381,7 @@ impl<'g, T: ?Sized + Pointable> Shared<'g, T> {
13661381
/// # unsafe { drop(a.into_owned()); } // avoid leak
13671382
/// ```
13681383
pub unsafe fn deref_mut(&mut self) -> &'g mut T {
1369-
let (raw, _) = decompose_tag::<T>(self.data);
1370-
unsafe { T::deref_mut(raw) }
1384+
unsafe { &mut *self.as_mut_ptr() }
13711385
}
13721386

13731387
/// Converts the pointer to a reference.
@@ -1407,7 +1421,7 @@ impl<'g, T: ?Sized + Pointable> Shared<'g, T> {
14071421
if raw.is_null() {
14081422
None
14091423
} else {
1410-
Some(unsafe { T::deref(raw) })
1424+
Some(unsafe { &*T::as_ptr(raw) })
14111425
}
14121426
}
14131427

@@ -1569,7 +1583,7 @@ impl<T: ?Sized + Pointable> fmt::Debug for Shared<'_, T> {
15691583

15701584
impl<T: ?Sized + Pointable> fmt::Pointer for Shared<'_, T> {
15711585
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1572-
fmt::Pointer::fmt(&(unsafe { self.deref() as *const _ }), f)
1586+
fmt::Pointer::fmt(&(unsafe { self.as_ptr() }), f)
15731587
}
15741588
}
15751589

crossbeam-epoch/src/internal.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,9 @@ pub(crate) struct Local {
292292
pin_count: Cell<Wrapping<usize>>,
293293

294294
/// The local epoch.
295-
epoch: CachePadded<AtomicEpoch>,
295+
// TODO
296+
// epoch: CachePadded<AtomicEpoch>,
297+
epoch: AtomicEpoch,
296298
}
297299

298300
// Make sure `Local` is less than or equal to 2048 bytes.
@@ -324,7 +326,9 @@ impl Local {
324326
guard_count: Cell::new(0),
325327
handle_count: Cell::new(1),
326328
pin_count: Cell::new(Wrapping(0)),
327-
epoch: CachePadded::new(AtomicEpoch::new(Epoch::starting())),
329+
// TODO
330+
// epoch: CachePadded::new(AtomicEpoch::new(Epoch::starting())),
331+
epoch: AtomicEpoch::new(Epoch::starting()),
328332
})
329333
.into_shared(unprotected());
330334
collector.global.locals.insert(local, unprotected());
@@ -535,24 +539,18 @@ impl Local {
535539
}
536540

537541
impl IsElement<Self> for Local {
538-
fn entry_of(local: &Self) -> &Entry {
542+
fn entry_of(local: *const Self) -> *const Entry {
539543
// SAFETY: `Local` is `repr(C)` and `entry` is the first field of it.
540-
unsafe {
541-
let entry_ptr = (local as *const Self).cast::<Entry>();
542-
&*entry_ptr
543-
}
544+
local.cast::<Entry>()
544545
}
545546

546-
unsafe fn element_of(entry: &Entry) -> &Self {
547+
unsafe fn element_of(entry: *const Entry) -> *const Self {
547548
// SAFETY: `Local` is `repr(C)` and `entry` is the first field of it.
548-
unsafe {
549-
let local_ptr = (entry as *const Entry).cast::<Self>();
550-
&*local_ptr
551-
}
549+
entry.cast::<Self>()
552550
}
553551

554-
unsafe fn finalize(entry: &Entry, guard: &Guard) {
555-
unsafe { guard.defer_destroy(Shared::from(Self::element_of(entry) as *const _)) }
552+
unsafe fn finalize(entry: *const Entry, guard: &Guard) {
553+
unsafe { guard.defer_destroy(Shared::from(Self::element_of(entry))) }
556554
}
557555
}
558556

crossbeam-epoch/src/sync/list.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! 2002. <http://dl.acm.org/citation.cfm?id=564870.564881>
55
66
use core::marker::PhantomData;
7+
use core::ptr::NonNull;
78
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release};
89

910
use crate::{unprotected, Atomic, Guard, Shared};
@@ -66,7 +67,7 @@ pub(crate) struct Entry {
6667
///
6768
pub(crate) trait IsElement<T> {
6869
/// Returns a reference to this element's `Entry`.
69-
fn entry_of(_: &T) -> &Entry;
70+
fn entry_of(_: *const T) -> *const Entry;
7071

7172
/// Given a reference to an element's entry, returns that element.
7273
///
@@ -80,15 +81,15 @@ pub(crate) trait IsElement<T> {
8081
///
8182
/// The caller has to guarantee that the `Entry` is called with was retrieved from an instance
8283
/// of the element type (`T`).
83-
unsafe fn element_of(_: &Entry) -> &T;
84+
unsafe fn element_of(_: *const Entry) -> *const T;
8485

8586
/// The function that is called when an entry is unlinked from list.
8687
///
8788
/// # Safety
8889
///
8990
/// The caller has to guarantee that the `Entry` is called with was retrieved from an instance
9091
/// of the element type (`T`).
91-
unsafe fn finalize(_: &Entry, _: &Guard);
92+
unsafe fn finalize(_: *const Entry, _: &Guard);
9293
}
9394

9495
/// A lock-free, intrusive linked list of type `T`.
@@ -173,16 +174,16 @@ impl<T, C: IsElement<T>> List<T, C> {
173174
// Insert right after head, i.e. at the beginning of the list.
174175
let to = &self.head;
175176
// Get the intrusively stored Entry of the new element to insert.
176-
let entry: &Entry = C::entry_of(unsafe { container.deref() });
177+
let entry: *const Entry = C::entry_of(unsafe { container.as_ptr() });
177178
// Make a Shared ptr to that Entry.
178-
let entry_ptr = Shared::from(entry as *const _);
179+
let entry_ptr = Shared::from(entry);
179180
// Read the current successor of where we want to insert.
180181
let mut next = to.load(Relaxed, guard);
181182

182183
loop {
183184
// Set the Entry of the to-be-inserted element to point to the previous successor of
184185
// `to`.
185-
entry.next.store(next, Relaxed);
186+
unsafe { (*entry).next.store(next, Relaxed) }
186187
match to.compare_exchange_weak(next, entry_ptr, Release, Relaxed, guard) {
187188
Ok(_) => break,
188189
// We lost the race or weak CAS failed spuriously. Update the successor and try
@@ -220,12 +221,12 @@ impl<T, C: IsElement<T>> Drop for List<T, C> {
220221
unsafe {
221222
let guard = unprotected();
222223
let mut curr = self.head.load(Relaxed, guard);
223-
while let Some(c) = curr.as_ref() {
224-
let succ = c.next.load(Relaxed, guard);
224+
while let Some(c) = NonNull::new(curr.as_ptr() as *mut Entry) {
225+
let succ = c.as_ref().next.load(Relaxed, guard);
225226
// Verify that all elements have been removed from the list.
226227
assert_eq!(succ.tag(), 1);
227228

228-
C::finalize(curr.deref(), guard);
229+
C::finalize(curr.as_ptr(), guard);
229230
curr = succ;
230231
}
231232
}
@@ -236,8 +237,8 @@ impl<'g, T: 'g, C: IsElement<T>> Iterator for Iter<'g, T, C> {
236237
type Item = Result<&'g T, IterError>;
237238

238239
fn next(&mut self) -> Option<Self::Item> {
239-
while let Some(c) = unsafe { self.curr.as_ref() } {
240-
let succ = c.next.load(Acquire, self.guard);
240+
while let Some(c) = unsafe { NonNull::new(self.curr.as_ptr() as *mut Entry) } {
241+
let succ = unsafe { c.as_ref().next.load(Acquire, self.guard) };
241242

242243
if succ.tag() == 1 {
243244
// This entry was removed. Try unlinking it from the list.
@@ -257,7 +258,7 @@ impl<'g, T: 'g, C: IsElement<T>> Iterator for Iter<'g, T, C> {
257258
// deallocation. Deferred drop is okay, because `list.delete()` can only be
258259
// called if `T: 'static`.
259260
unsafe {
260-
C::finalize(self.curr.deref(), self.guard);
261+
C::finalize(self.curr.as_ptr(), self.guard);
261262
}
262263

263264
// `succ` is the new value of `self.pred`.
@@ -284,10 +285,10 @@ impl<'g, T: 'g, C: IsElement<T>> Iterator for Iter<'g, T, C> {
284285
}
285286

286287
// Move one step forward.
287-
self.pred = &c.next;
288+
self.pred = unsafe { &(*c.as_ptr()).next };
288289
self.curr = succ;
289290

290-
return Some(Ok(unsafe { C::element_of(c) }));
291+
return Some(Ok(unsafe { &*C::element_of(c.as_ptr()) }));
291292
}
292293

293294
// We reached the end of the list.
@@ -303,16 +304,16 @@ mod tests {
303304
use std::sync::Barrier;
304305

305306
impl IsElement<Self> for Entry {
306-
fn entry_of(entry: &Self) -> &Entry {
307+
fn entry_of(entry: *const Self) -> *const Entry {
307308
entry
308309
}
309310

310-
unsafe fn element_of(entry: &Entry) -> &Self {
311+
unsafe fn element_of(entry: *const Entry) -> *const Self {
311312
entry
312313
}
313314

314-
unsafe fn finalize(entry: &Entry, guard: &Guard) {
315-
unsafe { guard.defer_destroy(Shared::from(Self::element_of(entry) as *const _)) }
315+
unsafe fn finalize(entry: *const Entry, guard: &Guard) {
316+
unsafe { guard.defer_destroy(Shared::from(Self::element_of(entry))) }
316317
}
317318
}
318319

0 commit comments

Comments
 (0)