Change:
In libstd/sys/unix there are numerous cases where return code is checked for error using following pattern: That means, that error checking is performed only when compiled in non-optimized mode or after explicitly enabling debug assertions. This is a little bit worrying, especially when used on platforms where those functions can indeed fail and break safety.
(For anyone who was confused by the given example, pthreadmutexattrinit can fail with ENOMEM.)
On some platforms types like pthreadmutext, pthreadcondt, etc., are "fat" structures that have sufficient size to hold everything necessary and their initialization will never fail (unless initialized incorrectly). But on others they are opaque pointers to and require an additional memory allocation. Search for those typedefs in libc crate, gives a rough approximation of affected platforms. Sadly this also affects static mutexes, so even lock / unlock could potentially fail. Either way you could end up with a mutex which is not mutually exclusive at all.
When mutex initialization fails this leads to non-functioning mutex and unsoundness. The pthreadmutexlock might also fail for recursive mutexes used in std::io and cause a mismatch in the number of locks / unlocks, but one needs to go out of the way to cause that. modify labels: C-bug I-unsound

Discussion:
} } <ins> pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> { if error == 0 { Ok(()) } else { Err(crate::io::Error::from_raw_os_error(error)) } } </ins> // On Unix-like platforms, libc::abort will unregister signal handlers // including the SIGABRT handler, preventing the abort from being blocked, and // fclose streams, with the side effect of flushing them so libc buffered

use crate::cell::UnsafeCell; use crate::mem::MaybeUninit; <ins> use crate::sys::cvt_nz; </ins> pub struct Mutex { inner: UnsafeCell<libc::pthread_mutex_t>,

// PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to // re-lock it from the same thread, thus avoiding undefined behavior. let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); <del> let r = libc::pthread_mutexattr_init(attr.as_mut_ptr()); debug_assert_eq!(r, 0); let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL); debug_assert_eq!(r, 0); let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); debug_assert_eq!(r, 0); let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); debug_assert_eq!(r, 0); </del> <ins> cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); let attr = PthreadMutexAttr(&mut attr); cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL)) .unwrap(); cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); </ins> } #[inline] pub unsafe fn lock(&self) {

pub unsafe fn init(&self) { let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); <del> let result = libc::pthread_mutexattr_init(attr.as_mut_ptr()); debug_assert_eq!(result, 0); let result = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE); debug_assert_eq!(result, 0); let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); debug_assert_eq!(result, 0); let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); debug_assert_eq!(result, 0); </del> <ins> cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); let attr = PthreadMutexAttr(&mut attr); cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE)) .unwrap(); cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); </ins> } pub unsafe fn lock(&self) {

debug_assert_eq!(result, 0); } } <ins> struct PthreadMutexAttr<'a>(&'a mut MaybeUninit<libc::pthread_mutexattr_t>); impl Drop for PthreadMutexAttr<'_> { fn drop(&mut self) { unsafe { let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); debug_assert_eq!(result, 0); } } } </ins>

envp: Option<&CStringArray>, ) -> io::Result<Option<Process>> { use crate::mem::MaybeUninit; <del> use crate::sys; </del> <ins> use crate::sys::{self, cvt_nz}; </ins> if self.get_gid().is_some() || self.get_uid().is_some()

} } <del> fn cvt_nz(error: libc::c_int) -> io::Result<()> { if error == 0 { Ok(()) } else { Err(io::Error::from_raw_os_error(error)) } } </del> unsafe { let mut attrs = MaybeUninit::uninit(); cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?;