Skip to content

Commit

Permalink
Rollup merge of rust-lang#98194 - m-ou-se:leak-locked-pthread-mutex, …
Browse files Browse the repository at this point in the history
…r=Amanieu

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

Fixes rust-lang#85434.
  • Loading branch information
JohnTitor authored Jun 25, 2022
2 parents 0104326 + e642c59 commit f6c1d26
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
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) });
}
}
}

0 comments on commit f6c1d26

Please sign in to comment.