Skip to content

Commit 0ffaaa0

Browse files
committed
Make BiLock strict-provenance compatible (#2716)
1 parent 4266221 commit 0ffaaa0

File tree

1 file changed

+30
-16
lines changed

1 file changed

+30
-16
lines changed

Diff for: futures-util/src/lock/bilock.rs

+30-16
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
use alloc::boxed::Box;
44
use alloc::sync::Arc;
55
use core::cell::UnsafeCell;
6-
use core::fmt;
76
use core::ops::{Deref, DerefMut};
87
use core::pin::Pin;
9-
use core::sync::atomic::AtomicUsize;
8+
use core::sync::atomic::AtomicPtr;
109
use core::sync::atomic::Ordering::SeqCst;
10+
use core::{fmt, ptr};
1111
#[cfg(feature = "bilock")]
1212
use futures_core::future::Future;
1313
use futures_core::task::{Context, Poll, Waker};
@@ -41,7 +41,7 @@ pub struct BiLock<T> {
4141

4242
#[derive(Debug)]
4343
struct Inner<T> {
44-
state: AtomicUsize,
44+
state: AtomicPtr<Waker>,
4545
value: Option<UnsafeCell<T>>,
4646
}
4747

@@ -61,7 +61,10 @@ impl<T> BiLock<T> {
6161
/// Similarly, reuniting the lock and extracting the inner value is only
6262
/// possible when `T` is `Unpin`.
6363
pub fn new(t: T) -> (Self, Self) {
64-
let arc = Arc::new(Inner { state: AtomicUsize::new(0), value: Some(UnsafeCell::new(t)) });
64+
let arc = Arc::new(Inner {
65+
state: AtomicPtr::new(ptr::null_mut()),
66+
value: Some(UnsafeCell::new(t)),
67+
});
6568

6669
(Self { arc: arc.clone() }, Self { arc })
6770
}
@@ -87,7 +90,8 @@ impl<T> BiLock<T> {
8790
pub fn poll_lock(&self, cx: &mut Context<'_>) -> Poll<BiLockGuard<'_, T>> {
8891
let mut waker = None;
8992
loop {
90-
match self.arc.state.swap(1, SeqCst) {
93+
let n = self.arc.state.swap(invalid_ptr(1), SeqCst);
94+
match n as usize {
9195
// Woohoo, we grabbed the lock!
9296
0 => return Poll::Ready(BiLockGuard { bilock: self }),
9397

@@ -96,27 +100,27 @@ impl<T> BiLock<T> {
96100

97101
// A task was previously blocked on this lock, likely our task,
98102
// so we need to update that task.
99-
n => unsafe {
100-
let mut prev = Box::from_raw(n as *mut Waker);
103+
_ => unsafe {
104+
let mut prev = Box::from_raw(n);
101105
*prev = cx.waker().clone();
102106
waker = Some(prev);
103107
},
104108
}
105109

106110
// type ascription for safety's sake!
107111
let me: Box<Waker> = waker.take().unwrap_or_else(|| Box::new(cx.waker().clone()));
108-
let me = Box::into_raw(me) as usize;
112+
let me = Box::into_raw(me);
109113

110-
match self.arc.state.compare_exchange(1, me, SeqCst, SeqCst) {
114+
match self.arc.state.compare_exchange(invalid_ptr(1), me, SeqCst, SeqCst) {
111115
// The lock is still locked, but we've now parked ourselves, so
112116
// just report that we're scheduled to receive a notification.
113117
Ok(_) => return Poll::Pending,
114118

115119
// Oops, looks like the lock was unlocked after our swap above
116120
// and before the compare_exchange. Deallocate what we just
117121
// allocated and go through the loop again.
118-
Err(0) => unsafe {
119-
waker = Some(Box::from_raw(me as *mut Waker));
122+
Err(n) if n.is_null() => unsafe {
123+
waker = Some(Box::from_raw(me));
120124
},
121125

122126
// The top of this loop set the previous state to 1, so if we
@@ -125,7 +129,7 @@ impl<T> BiLock<T> {
125129
// but we're trying to acquire the lock and there's only one
126130
// other reference of the lock, so it should be impossible for
127131
// that task to ever block itself.
128-
Err(n) => panic!("invalid state: {}", n),
132+
Err(n) => panic!("invalid state: {}", n as usize),
129133
}
130134
}
131135
}
@@ -164,7 +168,8 @@ impl<T> BiLock<T> {
164168
}
165169

166170
fn unlock(&self) {
167-
match self.arc.state.swap(0, SeqCst) {
171+
let n = self.arc.state.swap(ptr::null_mut(), SeqCst);
172+
match n as usize {
168173
// we've locked the lock, shouldn't be possible for us to see an
169174
// unlocked lock.
170175
0 => panic!("invalid unlocked state"),
@@ -174,8 +179,8 @@ impl<T> BiLock<T> {
174179

175180
// Another task has parked themselves on this lock, let's wake them
176181
// up as its now their turn.
177-
n => unsafe {
178-
Box::from_raw(n as *mut Waker).wake();
182+
_ => unsafe {
183+
Box::from_raw(n).wake();
179184
},
180185
}
181186
}
@@ -189,7 +194,7 @@ impl<T: Unpin> Inner<T> {
189194

190195
impl<T> Drop for Inner<T> {
191196
fn drop(&mut self) {
192-
assert_eq!(self.state.load(SeqCst), 0);
197+
assert!(self.state.load(SeqCst).is_null());
193198
}
194199
}
195200

@@ -277,3 +282,12 @@ impl<'a, T> Future for BiLockAcquire<'a, T> {
277282
self.bilock.poll_lock(cx)
278283
}
279284
}
285+
286+
// Based on core::ptr::invalid_mut. Equivalent to `addr as *mut T`, but is strict-provenance compatible.
287+
#[allow(clippy::useless_transmute)]
288+
#[inline]
289+
fn invalid_ptr<T>(addr: usize) -> *mut T {
290+
// SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that
291+
// pointer).
292+
unsafe { core::mem::transmute(addr) }
293+
}

0 commit comments

Comments
 (0)