diff --git a/src/error.rs b/src/error.rs index 5eff99eb..5c4f1c9e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -58,6 +58,8 @@ impl Error { pub const NODE_ES_MODULE: Error = internal_error(14); /// Calling Windows ProcessPrng failed. pub const WINDOWS_PROCESS_PRNG: Error = internal_error(15); + /// The mutex used when opening the random file was poisoned. + pub const UNEXPECTED_FILE_MUTEX_POISONED: Error = internal_error(16); /// Codes below this point represent OS Errors (i.e. positive i32 values). /// Codes at or above this point, but below [`Error::CUSTOM_START`] are @@ -175,6 +177,7 @@ fn internal_desc(error: Error) -> Option<&'static str> { Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"), Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"), Error::WINDOWS_PROCESS_PRNG => Some("ProcessPrng: Windows system function failure"), + Error::UNEXPECTED_FILE_MUTEX_POISONED => Some("File: Initialization panicked, poisoning the mutex"), _ => None, } } diff --git a/src/use_file.rs b/src/use_file.rs index 4505c0d1..c04dd3cb 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -4,7 +4,6 @@ extern crate std; use crate::{util_libc::sys_fill_exact, Error}; use core::{ - cell::UnsafeCell, ffi::c_void, mem::MaybeUninit, sync::atomic::{AtomicI32, Ordering}, @@ -14,6 +13,7 @@ use std::{ io, // TODO(MSRV 1.66): use `std::os::fd` instead of `std::unix::io`. os::unix::io::{AsRawFd as _, BorrowedFd, IntoRawFd as _, RawFd}, + sync::{Mutex, PoisonError}, }; /// For all platforms, we use `/dev/urandom` rather than `/dev/random`. @@ -70,9 +70,16 @@ fn get_rng_fd() -> Result, Error> { // descriptors concurrently, which could run into the limit on the // number of open file descriptors. Our goal is to have no more than one // file descriptor open, ever. - static MUTEX: Mutex = Mutex::new(); - unsafe { MUTEX.lock() }; - let _guard = DropGuard(|| unsafe { MUTEX.unlock() }); + // + // We assume any call to `Mutex::lock` synchronizes-with + // (Ordering::Acquire) the preceding dropping of a `MutexGuard` that + // unlocks the mutex (Ordering::Release) and that `Mutex` doesn't have + // any special treatment for what's "inside" the mutex (the `T` in + // `Mutex`). See `https://github.com/rust-lang/rust/issues/126239. + static MUTEX: Mutex<()> = Mutex::new(()); + let _guard = MUTEX + .lock() + .map_err(|_: PoisonError<_>| Error::UNEXPECTED_FILE_MUTEX_POISONED)?; if let Some(fd) = get_fd() { return Ok(fd); @@ -168,29 +175,3 @@ fn map_io_error(err: io::Error) -> Error { } }) } - -struct Mutex(UnsafeCell); - -impl Mutex { - const fn new() -> Self { - Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)) - } - unsafe fn lock(&self) { - let r = libc::pthread_mutex_lock(self.0.get()); - debug_assert_eq!(r, 0); - } - unsafe fn unlock(&self) { - let r = libc::pthread_mutex_unlock(self.0.get()); - debug_assert_eq!(r, 0); - } -} - -unsafe impl Sync for Mutex {} - -struct DropGuard(F); - -impl Drop for DropGuard { - fn drop(&mut self) { - self.0() - } -}