Coverage Report

Created: 2025-08-28 06:46

/rust/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.16/src/use_file.rs
Line
Count
Source (jump to first uncovered line)
1
//! Implementations that just need to read from a file
2
use crate::{
3
    util_libc::{open_readonly, sys_fill_exact},
4
    Error,
5
};
6
use core::{
7
    cell::UnsafeCell,
8
    mem::MaybeUninit,
9
    sync::atomic::{AtomicUsize, Ordering::Relaxed},
10
};
11
12
/// For all platforms, we use `/dev/urandom` rather than `/dev/random`.
13
/// For more information see the linked man pages in lib.rs.
14
///   - On Linux, "/dev/urandom is preferred and sufficient in all use cases".
15
///   - On Redox, only /dev/urandom is provided.
16
///   - On AIX, /dev/urandom will "provide cryptographically secure output".
17
///   - On Haiku and QNX Neutrino they are identical.
18
const FILE_PATH: &str = "/dev/urandom\0";
19
const FD_UNINIT: usize = usize::max_value();
20
21
0
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
22
0
    let fd = get_rng_fd()?;
23
0
    sys_fill_exact(dest, |buf| unsafe {
24
0
        libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len())
25
0
    })
26
0
}
27
28
// Returns the file descriptor for the device file used to retrieve random
29
// bytes. The file will be opened exactly once. All subsequent calls will
30
// return the same file descriptor. This file descriptor is never closed.
31
0
fn get_rng_fd() -> Result<libc::c_int, Error> {
32
    static FD: AtomicUsize = AtomicUsize::new(FD_UNINIT);
33
0
    fn get_fd() -> Option<libc::c_int> {
34
0
        match FD.load(Relaxed) {
35
0
            FD_UNINIT => None,
36
0
            val => Some(val as libc::c_int),
37
        }
38
0
    }
39
40
    // Use double-checked locking to avoid acquiring the lock if possible.
41
0
    if let Some(fd) = get_fd() {
42
0
        return Ok(fd);
43
0
    }
44
45
    // SAFETY: We use the mutex only in this method, and we always unlock it
46
    // before returning, making sure we don't violate the pthread_mutex_t API.
47
    static MUTEX: Mutex = Mutex::new();
48
0
    unsafe { MUTEX.lock() };
49
0
    let _guard = DropGuard(|| unsafe { MUTEX.unlock() });
50
51
0
    if let Some(fd) = get_fd() {
52
0
        return Ok(fd);
53
0
    }
54
0
55
0
    // On Linux, /dev/urandom might return insecure values.
56
0
    #[cfg(any(target_os = "android", target_os = "linux"))]
57
0
    wait_until_rng_ready()?;
58
59
0
    let fd = unsafe { open_readonly(FILE_PATH)? };
60
    // The fd always fits in a usize without conflicting with FD_UNINIT.
61
0
    debug_assert!(fd >= 0 && (fd as usize) < FD_UNINIT);
62
0
    FD.store(fd as usize, Relaxed);
63
0
64
0
    Ok(fd)
65
0
}
66
67
// Succeeds once /dev/urandom is safe to read from
68
#[cfg(any(target_os = "android", target_os = "linux"))]
69
0
fn wait_until_rng_ready() -> Result<(), Error> {
70
    // Poll /dev/random to make sure it is ok to read from /dev/urandom.
71
0
    let fd = unsafe { open_readonly("/dev/random\0")? };
72
0
    let mut pfd = libc::pollfd {
73
0
        fd,
74
0
        events: libc::POLLIN,
75
0
        revents: 0,
76
0
    };
77
0
    let _guard = DropGuard(|| unsafe {
78
0
        libc::close(fd);
79
0
    });
80
81
    loop {
82
        // A negative timeout means an infinite timeout.
83
0
        let res = unsafe { libc::poll(&mut pfd, 1, -1) };
84
0
        if res >= 0 {
85
0
            debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout.
86
0
            return Ok(());
87
0
        }
88
0
        let err = crate::util_libc::last_os_error();
89
0
        match err.raw_os_error() {
90
0
            Some(libc::EINTR) | Some(libc::EAGAIN) => continue,
91
0
            _ => return Err(err),
92
        }
93
    }
94
0
}
95
96
struct Mutex(UnsafeCell<libc::pthread_mutex_t>);
97
98
impl Mutex {
99
0
    const fn new() -> Self {
100
0
        Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))
101
0
    }
102
0
    unsafe fn lock(&self) {
103
0
        let r = libc::pthread_mutex_lock(self.0.get());
104
0
        debug_assert_eq!(r, 0);
105
0
    }
106
0
    unsafe fn unlock(&self) {
107
0
        let r = libc::pthread_mutex_unlock(self.0.get());
108
0
        debug_assert_eq!(r, 0);
109
0
    }
110
}
111
112
unsafe impl Sync for Mutex {}
113
114
struct DropGuard<F: FnMut()>(F);
115
116
impl<F: FnMut()> Drop for DropGuard<F> {
117
0
    fn drop(&mut self) {
118
0
        self.0()
119
0
    }
Unexecuted instantiation: <getrandom::use_file::DropGuard<getrandom::use_file::get_rng_fd::{closure#0}> as core::ops::drop::Drop>::drop
Unexecuted instantiation: <getrandom::use_file::DropGuard<getrandom::use_file::wait_until_rng_ready::{closure#0}> as core::ops::drop::Drop>::drop
120
}