Skip to content
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

Leak pthread_{mutex,rwlock}_t if it's dropped while locked. #98194

Merged
merged 2 commits into from
Jun 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion library/std/src/sys/unix/locks/pthread_mutex.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::cell::UnsafeCell;
use crate::mem::MaybeUninit;
use crate::mem::{forget, MaybeUninit};
use crate::sys::cvt_nz;
use crate::sys_common::lazy_box::{LazyBox, LazyInit};

Expand All @@ -23,6 +23,24 @@ impl LazyInit for Mutex {
unsafe { mutex.init() };
mutex
}

fn destroy(mutex: Box<Self>) {
// We're not allowed to pthread_mutex_destroy a locked mutex,
// so check first if it's unlocked.
if unsafe { mutex.try_lock() } {
unsafe { mutex.unlock() };
drop(mutex);
} else {
// The mutex is locked. This happens if a MutexGuard is leaked.
// In this case, we just leak the Mutex too.
forget(mutex);
}
}

fn cancel_init(_: Box<Self>) {
// In this case, we can just drop it without any checks,
// since it cannot have been locked yet.
}
}

impl Mutex {
Expand Down
16 changes: 16 additions & 0 deletions library/std/src/sys/unix/locks/pthread_rwlock.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::cell::UnsafeCell;
use crate::mem::forget;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys_common::lazy_box::{LazyBox, LazyInit};

Expand All @@ -17,6 +18,21 @@ impl LazyInit for RwLock {
fn init() -> Box<Self> {
Box::new(Self::new())
}

fn destroy(mut rwlock: Box<Self>) {
// We're not allowed to pthread_rwlock_destroy a locked rwlock,
// so check first if it's unlocked.
if *rwlock.write_locked.get_mut() || *rwlock.num_readers.get_mut() != 0 {
// The rwlock is locked. This happens if a RwLock{Read,Write}Guard is leaked.
// In this case, we just leak the RwLock too.
forget(rwlock);
}
}

fn cancel_init(_: Box<Self>) {
// In this case, we can just drop it without any checks,
// since it cannot have been locked yet.
}
}

impl RwLock {
Expand Down
19 changes: 16 additions & 3 deletions library/std/src/sys_common/lazy_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,21 @@ pub(crate) trait LazyInit {
///
/// It might be called more than once per LazyBox, as multiple threads
/// might race to initialize it concurrently, each constructing and initializing
/// their own box. (All but one of them will be destroyed right after.)
/// their own box. All but one of them will be passed to `cancel_init` right after.
fn init() -> Box<Self>;

/// Any surplus boxes from `init()` that lost the initialization race
/// are passed to this function for disposal.
///
/// The default implementation calls destroy().
fn cancel_init(x: Box<Self>) {
Self::destroy(x);
}

/// This is called to destroy a used box.
///
/// The default implementation just drops it.
fn destroy(_: Box<Self>) {}
}

impl<T: LazyInit> LazyBox<T> {
Expand All @@ -45,7 +58,7 @@ impl<T: LazyInit> LazyBox<T> {
Err(ptr) => {
// Lost the race to another thread.
// Drop the box we created, and use the one from the other thread instead.
drop(unsafe { Box::from_raw(new_ptr) });
T::cancel_init(unsafe { Box::from_raw(new_ptr) });
ptr
}
}
Expand All @@ -71,7 +84,7 @@ impl<T: LazyInit> Drop for LazyBox<T> {
fn drop(&mut self) {
let ptr = *self.ptr.get_mut();
if !ptr.is_null() {
drop(unsafe { Box::from_raw(ptr) });
T::destroy(unsafe { Box::from_raw(ptr) });
}
}
}