Skip to content

Commit 3c4d157

Browse files
author
Jethro Beekman
committed
Fix unlock ordering in SGX synchronization primitives
1 parent 72b2abf commit 3c4d157

File tree

4 files changed

+32
-20
lines changed

4 files changed

+32
-20
lines changed

src/libstd/sys/sgx/condvar.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ impl Condvar {
2727

2828
pub unsafe fn wait(&self, mutex: &Mutex) {
2929
let guard = self.inner.lock();
30-
mutex.unlock();
31-
WaitQueue::wait(guard);
30+
WaitQueue::wait(guard, || mutex.unlock());
3231
mutex.lock()
3332
}
3433

src/libstd/sys/sgx/mutex.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl Mutex {
2222
let mut guard = self.inner.lock();
2323
if *guard.lock_var() {
2424
// Another thread has the lock, wait
25-
WaitQueue::wait(guard)
25+
WaitQueue::wait(guard, ||{})
2626
// Another thread has passed the lock to us
2727
} else {
2828
// We are just now obtaining the lock
@@ -83,7 +83,7 @@ impl ReentrantMutex {
8383
match guard.lock_var().owner {
8484
Some(tcs) if tcs != thread::current() => {
8585
// Another thread has the lock, wait
86-
WaitQueue::wait(guard);
86+
WaitQueue::wait(guard, ||{});
8787
// Another thread has passed the lock to us
8888
},
8989
_ => {

src/libstd/sys/sgx/rwlock.rs

+20-14
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl RWLock {
3131
if *wguard.lock_var() || !wguard.queue_empty() {
3232
// Another thread has or is waiting for the write lock, wait
3333
drop(wguard);
34-
WaitQueue::wait(rguard);
34+
WaitQueue::wait(rguard, ||{});
3535
// Another thread has passed the lock to us
3636
} else {
3737
// No waiting writers, acquire the read lock
@@ -62,7 +62,7 @@ impl RWLock {
6262
if *wguard.lock_var() || rguard.lock_var().is_some() {
6363
// Another thread has the lock, wait
6464
drop(rguard);
65-
WaitQueue::wait(wguard);
65+
WaitQueue::wait(wguard, ||{});
6666
// Another thread has passed the lock to us
6767
} else {
6868
// We are just now obtaining the lock
@@ -97,6 +97,7 @@ impl RWLock {
9797
if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
9898
// A writer was waiting, pass the lock
9999
*wguard.lock_var_mut() = true;
100+
wguard.drop_after(rguard);
100101
} else {
101102
// No writers were waiting, the lock is released
102103
rtassert!(rguard.queue_empty());
@@ -117,21 +118,26 @@ impl RWLock {
117118
rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
118119
wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
119120
) {
120-
if let Err(mut wguard) = WaitQueue::notify_one(wguard) {
121-
// No writers waiting, release the write lock
122-
*wguard.lock_var_mut() = false;
123-
if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
124-
// One or more readers were waiting, pass the lock to them
125-
if let NotifiedTcs::All { count } = rguard.notified_tcs() {
126-
*rguard.lock_var_mut() = Some(count)
121+
match WaitQueue::notify_one(wguard) {
122+
Err(mut wguard) => {
123+
// No writers waiting, release the write lock
124+
*wguard.lock_var_mut() = false;
125+
if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
126+
// One or more readers were waiting, pass the lock to them
127+
if let NotifiedTcs::All { count } = rguard.notified_tcs() {
128+
*rguard.lock_var_mut() = Some(count)
129+
} else {
130+
unreachable!() // called notify_all
131+
}
132+
rguard.drop_after(wguard);
127133
} else {
128-
unreachable!() // called notify_all
134+
// No readers waiting, the lock is released
129135
}
130-
} else {
131-
// No readers waiting, the lock is released
136+
},
137+
Ok(wguard) => {
138+
// There was a thread waiting for write, just pass the lock
139+
wguard.drop_after(rguard);
132140
}
133-
} else {
134-
// There was a thread waiting for write, just pass the lock
135141
}
136142
}
137143

src/libstd/sys/sgx/waitqueue.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ impl<'a, T> WaitGuard<'a, T> {
9898
pub fn notified_tcs(&self) -> NotifiedTcs {
9999
self.notified_tcs
100100
}
101+
102+
/// Drop this `WaitGuard`, after dropping another `guard`.
103+
pub fn drop_after<U>(self, guard: U) {
104+
drop(guard);
105+
drop(self);
106+
}
101107
}
102108

103109
impl<'a, T> Deref for WaitGuard<'a, T> {
@@ -140,7 +146,7 @@ impl WaitQueue {
140146
/// until a wakeup event.
141147
///
142148
/// This function does not return until this thread has been awoken.
143-
pub fn wait<T>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>) {
149+
pub fn wait<T, F: FnOnce()>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>, before_wait: F) {
144150
// very unsafe: check requirements of UnsafeList::push
145151
unsafe {
146152
let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
@@ -149,6 +155,7 @@ impl WaitQueue {
149155
}));
150156
let entry = guard.queue.inner.push(&mut entry);
151157
drop(guard);
158+
before_wait();
152159
while !entry.lock().wake {
153160
// don't panic, this would invalidate `entry` during unwinding
154161
let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE));
@@ -545,7 +552,7 @@ mod tests {
545552
assert!(WaitQueue::notify_one(wq2.lock()).is_ok());
546553
});
547554

548-
WaitQueue::wait(locked);
555+
WaitQueue::wait(locked, ||{});
549556

550557
t1.join().unwrap();
551558
}

0 commit comments

Comments
 (0)