Coverage Report

Created: 2025-12-12 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.2.7/src/use_file.rs
Line
Count
Source
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
    sync::atomic::{AtomicUsize, Ordering::Relaxed},
18
};
19
20
#[cfg(any(
21
    target_os = "dragonfly",
22
    target_os = "emscripten",
23
    target_os = "haiku",
24
    target_os = "macos",
25
    target_os = "solaris",
26
    target_os = "illumos"
27
))]
28
const FILE_PATH: &str = "/dev/random\0";
29
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
30
const FILE_PATH: &str = "/dev/urandom\0";
31
32
0
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
33
0
    let fd = get_rng_fd()?;
34
0
    let read = |buf: &mut [u8]| unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
35
36
0
    if cfg!(target_os = "emscripten") {
37
        // `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
38
0
        for chunk in dest.chunks_mut(65536) {
39
0
            sys_fill_exact(chunk, read)?;
40
        }
41
    } else {
42
0
        sys_fill_exact(dest, read)?;
43
    }
44
0
    Ok(())
45
0
}
46
47
// Returns the file descriptor for the device file used to retrieve random
48
// bytes. The file will be opened exactly once. All subsequent calls will
49
// return the same file descriptor. This file descriptor is never closed.
50
0
fn get_rng_fd() -> Result<libc::c_int, Error> {
51
    static FD: AtomicUsize = AtomicUsize::new(LazyUsize::UNINIT);
52
0
    fn get_fd() -> Option<libc::c_int> {
53
0
        match FD.load(Relaxed) {
54
0
            LazyUsize::UNINIT => None,
55
0
            val => Some(val as libc::c_int),
56
        }
57
0
    }
58
59
    // Use double-checked locking to avoid acquiring the lock if possible.
60
0
    if let Some(fd) = get_fd() {
61
0
        return Ok(fd);
62
0
    }
63
64
    // SAFETY: We use the mutex only in this method, and we always unlock it
65
    // before returning, making sure we don't violate the pthread_mutex_t API.
66
    static MUTEX: Mutex = Mutex::new();
67
0
    unsafe { MUTEX.lock() };
68
0
    let _guard = DropGuard(|| unsafe { MUTEX.unlock() });
69
70
0
    if let Some(fd) = get_fd() {
71
0
        return Ok(fd);
72
0
    }
73
74
    // On Linux, /dev/urandom might return insecure values.
75
    #[cfg(any(target_os = "android", target_os = "linux"))]
76
0
    wait_until_rng_ready()?;
77
78
0
    let fd = unsafe { open_readonly(FILE_PATH)? };
79
    // The fd always fits in a usize without conflicting with UNINIT.
80
0
    debug_assert!(fd >= 0 && (fd as usize) < LazyUsize::UNINIT);
81
0
    FD.store(fd as usize, Relaxed);
82
83
0
    Ok(fd)
84
0
}
85
86
// Succeeds once /dev/urandom is safe to read from
87
#[cfg(any(target_os = "android", target_os = "linux"))]
88
0
fn wait_until_rng_ready() -> Result<(), Error> {
89
    // Poll /dev/random to make sure it is ok to read from /dev/urandom.
90
0
    let fd = unsafe { open_readonly("/dev/random\0")? };
91
0
    let mut pfd = libc::pollfd {
92
0
        fd,
93
0
        events: libc::POLLIN,
94
0
        revents: 0,
95
0
    };
96
0
    let _guard = DropGuard(|| unsafe {
97
0
        libc::close(fd);
98
0
    });
99
100
    loop {
101
        // A negative timeout means an infinite timeout.
102
0
        let res = unsafe { libc::poll(&mut pfd, 1, -1) };
103
0
        if res >= 0 {
104
0
            debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout.
105
0
            return Ok(());
106
0
        }
107
0
        let err = crate::util_libc::last_os_error();
108
0
        match err.raw_os_error() {
109
0
            Some(libc::EINTR) | Some(libc::EAGAIN) => continue,
110
0
            _ => return Err(err),
111
        }
112
    }
113
0
}
114
115
struct Mutex(UnsafeCell<libc::pthread_mutex_t>);
116
117
impl Mutex {
118
0
    const fn new() -> Self {
119
0
        Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))
120
0
    }
121
0
    unsafe fn lock(&self) {
122
0
        let r = libc::pthread_mutex_lock(self.0.get());
123
0
        debug_assert_eq!(r, 0);
124
0
    }
125
0
    unsafe fn unlock(&self) {
126
0
        let r = libc::pthread_mutex_unlock(self.0.get());
127
0
        debug_assert_eq!(r, 0);
128
0
    }
129
}
130
131
unsafe impl Sync for Mutex {}
132
133
struct DropGuard<F: FnMut()>(F);
134
135
impl<F: FnMut()> Drop for DropGuard<F> {
136
0
    fn drop(&mut self) {
137
0
        self.0()
138
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
139
}