Coverage Report

Created: 2025-09-04 06:37

/rust/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.10/src/use_file.rs
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2018 Developers of the Rand project.
2
//
3
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6
// option. This file may not be copied, modified, or distributed
7
// except according to those terms.
8
9
//! Implementations that just need to read from a file
10
use crate::{
11
    util::LazyUsize,
12
    util_libc::{open_readonly, sys_fill_exact},
13
    Error,
14
};
15
use core::{
16
    cell::UnsafeCell,
17
    mem::MaybeUninit,
18
    sync::atomic::{AtomicUsize, Ordering::Relaxed},
19
};
20
21
// We prefer using /dev/urandom and only use /dev/random if the OS
22
// documentation indicates that /dev/urandom is insecure.
23
// On Solaris/Illumos, see src/solaris_illumos.rs
24
// On Dragonfly, Haiku, macOS, and QNX Neutrino the devices are identical.
25
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
26
const FILE_PATH: &str = "/dev/random\0";
27
#[cfg(any(
28
    target_os = "aix",
29
    target_os = "android",
30
    target_os = "linux",
31
    target_os = "redox",
32
    target_os = "dragonfly",
33
    target_os = "haiku",
34
    target_os = "macos",
35
    target_os = "nto",
36
))]
37
const FILE_PATH: &str = "/dev/urandom\0";
38
39
0
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
40
0
    let fd = get_rng_fd()?;
41
0
    sys_fill_exact(dest, |buf| unsafe {
42
0
        libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len())
43
0
    })
44
0
}
45
46
// Returns the file descriptor for the device file used to retrieve random
47
// bytes. The file will be opened exactly once. All subsequent calls will
48
// return the same file descriptor. This file descriptor is never closed.
49
0
fn get_rng_fd() -> Result<libc::c_int, Error> {
50
    static FD: AtomicUsize = AtomicUsize::new(LazyUsize::UNINIT);
51
0
    fn get_fd() -> Option<libc::c_int> {
52
0
        match FD.load(Relaxed) {
53
0
            LazyUsize::UNINIT => None,
54
0
            val => Some(val as libc::c_int),
55
        }
56
0
    }
57
58
    // Use double-checked locking to avoid acquiring the lock if possible.
59
0
    if let Some(fd) = get_fd() {
60
0
        return Ok(fd);
61
0
    }
62
63
    // SAFETY: We use the mutex only in this method, and we always unlock it
64
    // before returning, making sure we don't violate the pthread_mutex_t API.
65
    static MUTEX: Mutex = Mutex::new();
66
0
    unsafe { MUTEX.lock() };
67
0
    let _guard = DropGuard(|| unsafe { MUTEX.unlock() });
68
69
0
    if let Some(fd) = get_fd() {
70
0
        return Ok(fd);
71
0
    }
72
0
73
0
    // On Linux, /dev/urandom might return insecure values.
74
0
    #[cfg(any(target_os = "android", target_os = "linux"))]
75
0
    wait_until_rng_ready()?;
76
77
0
    let fd = unsafe { open_readonly(FILE_PATH)? };
78
    // The fd always fits in a usize without conflicting with UNINIT.
79
0
    debug_assert!(fd >= 0 && (fd as usize) < LazyUsize::UNINIT);
80
0
    FD.store(fd as usize, Relaxed);
81
0
82
0
    Ok(fd)
83
0
}
84
85
// Succeeds once /dev/urandom is safe to read from
86
#[cfg(any(target_os = "android", target_os = "linux"))]
87
0
fn wait_until_rng_ready() -> Result<(), Error> {
88
    // Poll /dev/random to make sure it is ok to read from /dev/urandom.
89
0
    let fd = unsafe { open_readonly("/dev/random\0")? };
90
0
    let mut pfd = libc::pollfd {
91
0
        fd,
92
0
        events: libc::POLLIN,
93
0
        revents: 0,
94
0
    };
95
0
    let _guard = DropGuard(|| unsafe {
96
0
        libc::close(fd);
97
0
    });
98
99
    loop {
100
        // A negative timeout means an infinite timeout.
101
0
        let res = unsafe { libc::poll(&mut pfd, 1, -1) };
102
0
        if res >= 0 {
103
0
            debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout.
104
0
            return Ok(());
105
0
        }
106
0
        let err = crate::util_libc::last_os_error();
107
0
        match err.raw_os_error() {
108
0
            Some(libc::EINTR) | Some(libc::EAGAIN) => continue,
109
0
            _ => return Err(err),
110
        }
111
    }
112
0
}
113
114
struct Mutex(UnsafeCell<libc::pthread_mutex_t>);
115
116
impl Mutex {
117
0
    const fn new() -> Self {
118
0
        Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))
119
0
    }
120
0
    unsafe fn lock(&self) {
121
0
        let r = libc::pthread_mutex_lock(self.0.get());
122
0
        debug_assert_eq!(r, 0);
123
0
    }
124
0
    unsafe fn unlock(&self) {
125
0
        let r = libc::pthread_mutex_unlock(self.0.get());
126
0
        debug_assert_eq!(r, 0);
127
0
    }
128
}
129
130
unsafe impl Sync for Mutex {}
131
132
struct DropGuard<F: FnMut()>(F);
133
134
impl<F: FnMut()> Drop for DropGuard<F> {
135
0
    fn drop(&mut self) {
136
0
        self.0()
137
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
138
}