From d72294491c5d28449c49b884b620cc24b3cf010f Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 16 Jun 2022 11:23:31 +0200 Subject: [PATCH 1/2] Leak pthreax_mutex_t when it's dropped while locked. --- .../std/src/sys/unix/locks/pthread_mutex.rs | 20 ++++++++++++++++++- library/std/src/sys_common/lazy_box.rs | 19 +++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs index 916e898d8906e..98afee69ba622 100644 --- a/library/std/src/sys/unix/locks/pthread_mutex.rs +++ b/library/std/src/sys/unix/locks/pthread_mutex.rs @@ -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}; @@ -23,6 +23,24 @@ impl LazyInit for Mutex { unsafe { mutex.init() }; mutex } + + fn destroy(mutex: Box) { + // 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) { + // In this case, we can just drop it without any checks, + // since it cannot have been locked yet. + } } impl Mutex { diff --git a/library/std/src/sys_common/lazy_box.rs b/library/std/src/sys_common/lazy_box.rs index 647c13d243724..63c3316bdeb28 100644 --- a/library/std/src/sys_common/lazy_box.rs +++ b/library/std/src/sys_common/lazy_box.rs @@ -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; + + /// 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::destroy(x); + } + + /// This is called to destroy a used box. + /// + /// The default implementation just drops it. + fn destroy(_: Box) {} } impl LazyBox { @@ -45,7 +58,7 @@ impl LazyBox { 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 } } @@ -71,7 +84,7 @@ impl Drop for LazyBox { 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) }); } } } From e642c5987e1885a6ea9b0f1527810a72bdcdeb3f Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sun, 19 Jun 2022 12:53:12 +0200 Subject: [PATCH 2/2] Leak pthreax_rwlock_t when it's dropped while locked. --- library/std/src/sys/unix/locks/pthread_rwlock.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/library/std/src/sys/unix/locks/pthread_rwlock.rs b/library/std/src/sys/unix/locks/pthread_rwlock.rs index 75e5759c7879d..adfe2a88338f5 100644 --- a/library/std/src/sys/unix/locks/pthread_rwlock.rs +++ b/library/std/src/sys/unix/locks/pthread_rwlock.rs @@ -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}; @@ -17,6 +18,21 @@ impl LazyInit for RwLock { fn init() -> Box { Box::new(Self::new()) } + + fn destroy(mut rwlock: Box) { + // 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) { + // In this case, we can just drop it without any checks, + // since it cannot have been locked yet. + } } impl RwLock {