Skip to content

Commit f9103ba

Browse files
authoredJun 25, 2022
Rollup merge of #98194 - m-ou-se:leak-locked-pthread-mutex, r=Amanieu
Leak pthread_{mutex,rwlock}_t if it's dropped while locked. Fixes rust-lang/rust#85434.
2 parents acbceb2 + 6888684 commit f9103ba

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed
 

‎std/src/sys/unix/locks/pthread_mutex.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::cell::UnsafeCell;
2-
use crate::mem::MaybeUninit;
2+
use crate::mem::{forget, MaybeUninit};
33
use crate::sys::cvt_nz;
44
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
55

@@ -23,6 +23,24 @@ impl LazyInit for Mutex {
2323
unsafe { mutex.init() };
2424
mutex
2525
}
26+
27+
fn destroy(mutex: Box<Self>) {
28+
// We're not allowed to pthread_mutex_destroy a locked mutex,
29+
// so check first if it's unlocked.
30+
if unsafe { mutex.try_lock() } {
31+
unsafe { mutex.unlock() };
32+
drop(mutex);
33+
} else {
34+
// The mutex is locked. This happens if a MutexGuard is leaked.
35+
// In this case, we just leak the Mutex too.
36+
forget(mutex);
37+
}
38+
}
39+
40+
fn cancel_init(_: Box<Self>) {
41+
// In this case, we can just drop it without any checks,
42+
// since it cannot have been locked yet.
43+
}
2644
}
2745

2846
impl Mutex {

‎std/src/sys/unix/locks/pthread_rwlock.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::cell::UnsafeCell;
2+
use crate::mem::forget;
23
use crate::sync::atomic::{AtomicUsize, Ordering};
34
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
45

@@ -17,6 +18,21 @@ impl LazyInit for RwLock {
1718
fn init() -> Box<Self> {
1819
Box::new(Self::new())
1920
}
21+
22+
fn destroy(mut rwlock: Box<Self>) {
23+
// We're not allowed to pthread_rwlock_destroy a locked rwlock,
24+
// so check first if it's unlocked.
25+
if *rwlock.write_locked.get_mut() || *rwlock.num_readers.get_mut() != 0 {
26+
// The rwlock is locked. This happens if a RwLock{Read,Write}Guard is leaked.
27+
// In this case, we just leak the RwLock too.
28+
forget(rwlock);
29+
}
30+
}
31+
32+
fn cancel_init(_: Box<Self>) {
33+
// In this case, we can just drop it without any checks,
34+
// since it cannot have been locked yet.
35+
}
2036
}
2137

2238
impl RwLock {

‎std/src/sys_common/lazy_box.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,21 @@ pub(crate) trait LazyInit {
2121
///
2222
/// It might be called more than once per LazyBox, as multiple threads
2323
/// might race to initialize it concurrently, each constructing and initializing
24-
/// their own box. (All but one of them will be destroyed right after.)
24+
/// their own box. All but one of them will be passed to `cancel_init` right after.
2525
fn init() -> Box<Self>;
26+
27+
/// Any surplus boxes from `init()` that lost the initialization race
28+
/// are passed to this function for disposal.
29+
///
30+
/// The default implementation calls destroy().
31+
fn cancel_init(x: Box<Self>) {
32+
Self::destroy(x);
33+
}
34+
35+
/// This is called to destroy a used box.
36+
///
37+
/// The default implementation just drops it.
38+
fn destroy(_: Box<Self>) {}
2639
}
2740

2841
impl<T: LazyInit> LazyBox<T> {
@@ -45,7 +58,7 @@ impl<T: LazyInit> LazyBox<T> {
4558
Err(ptr) => {
4659
// Lost the race to another thread.
4760
// Drop the box we created, and use the one from the other thread instead.
48-
drop(unsafe { Box::from_raw(new_ptr) });
61+
T::cancel_init(unsafe { Box::from_raw(new_ptr) });
4962
ptr
5063
}
5164
}
@@ -71,7 +84,7 @@ impl<T: LazyInit> Drop for LazyBox<T> {
7184
fn drop(&mut self) {
7285
let ptr = *self.ptr.get_mut();
7386
if !ptr.is_null() {
74-
drop(unsafe { Box::from_raw(ptr) });
87+
T::destroy(unsafe { Box::from_raw(ptr) });
7588
}
7689
}
7790
}

0 commit comments

Comments
 (0)
Please sign in to comment.