-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NoopLock is unsound #42
Comments
I was a little bit scared that exposing it would have a side effect when reviewing #38 . I then decided it wasn't critical since usages like
So as long as you use |
But you can also lock a mutex twice on the same thread and gain two simultaneous |
Sample:
|
(I actually think this is "exploitable" without #38 too, since you can use generics to unify a |
I thought about this a bit more before pushing 0.4. It seems like there doesn't exist any thread-safety issues, since With that in mind the borrow-checking problem persists. That could be fixed by converting |
I thought about this issue again. NoopLock is also technically unsound even when used with futures-intrusive data structures like LocalMutex, because reentrancy is possible if a nonstandard Here's a contrived example: use std::cell::Cell;
use std::future::Future;
use std::ptr;
use std::rc::Rc;
use std::task::{Context, RawWaker, RawWakerVTable, Waker};
use futures::pin_mut;
use futures_intrusive::sync::LocalMutex as Mutex;
thread_local! {
static MUTEX: Cell<Option<Rc<Mutex<()>>>> = Cell::new(None);
}
const VTABLE: RawWakerVTable = RawWakerVTable::new(
|_| {
MUTEX.with(|m| {
if let Some(m) = m.take() {
m.try_lock();
}
});
RawWaker::new(ptr::null(), &VTABLE)
},
|_| (),
|_| (),
|_| (),
);
fn main() {
let waker = RawWaker::new(ptr::null(), &VTABLE);
let waker = unsafe { Waker::from_raw(waker) }; // safety: VTABLE meets the RawWaker invariants
let mutex = Rc::new(Mutex::new((), true));
let _lock = mutex.try_lock().unwrap();
MUTEX.with(|m| {
m.set(Some(mutex.clone()));
});
let f = mutex.lock();
pin_mut!(f);
let mut cx = Context::from_waker(&waker);
assert!(f.as_mut().poll(&mut cx).is_pending());
} This causes the MutexState to be locked twice simultaneously (you can tell because if you import the normal |
Since I had to push a new major revision anyway due to the parking lot version stuff I now simply made NoopLock back private. For the last example: This deadlocks happens because it tries to lock the synchronous parking lot Mutex 2 times in the same thread. With the singlethreaded variant (NoopLock) this doesn't happen, and the |
NoopLock
implementsRawMutex
, which is an unsafe trait with the requirementHowever,
NoopLock
does not provide any such guarantees.That means that a
lock_api::Mutex<NoopLock, T>
won't prevent multiple lock guards from being acquired simultaneously, which is unsound.The text was updated successfully, but these errors were encountered: