Skip to content

Commit b18ecbd

Browse files
authored
Rollup merge of rust-lang#53725 - tbu-:pr_getrandom_syscalls, r=alexcrichton
Reduce number of syscalls in `rand` This skips the initial zero-length `getrandom` call and directly hands the user buffer to the operating system, saving one `getrandom` syscall.
2 parents a22c6e4 + 6d47737 commit b18ecbd

File tree

1 file changed

+22
-39
lines changed

1 file changed

+22
-39
lines changed

src/libstd/sys/unix/rand.rs

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -40,71 +40,54 @@ mod imp {
4040
}
4141

4242
#[cfg(not(any(target_os = "linux", target_os = "android")))]
43-
fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
43+
fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false }
4444

45+
#[cfg(any(target_os = "linux", target_os = "android"))]
4546
fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
47+
use sync::atomic::{AtomicBool, Ordering};
48+
static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
49+
if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) {
50+
return false;
51+
}
52+
4653
let mut read = 0;
4754
while read < v.len() {
4855
let result = getrandom(&mut v[read..]);
4956
if result == -1 {
5057
let err = errno() as libc::c_int;
5158
if err == libc::EINTR {
5259
continue;
60+
} else if err == libc::ENOSYS {
61+
GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
5362
} else if err == libc::EAGAIN {
54-
return false
63+
return false;
5564
} else {
5665
panic!("unexpected getrandom error: {}", err);
5766
}
5867
} else {
5968
read += result as usize;
6069
}
6170
}
62-
63-
return true
64-
}
65-
66-
#[cfg(any(target_os = "linux", target_os = "android"))]
67-
fn is_getrandom_available() -> bool {
68-
use io;
69-
use sync::atomic::{AtomicBool, Ordering};
70-
use sync::Once;
71-
72-
static CHECKER: Once = Once::new();
73-
static AVAILABLE: AtomicBool = AtomicBool::new(false);
74-
75-
CHECKER.call_once(|| {
76-
let mut buf: [u8; 0] = [];
77-
let result = getrandom(&mut buf);
78-
let available = if result == -1 {
79-
let err = io::Error::last_os_error().raw_os_error();
80-
err != Some(libc::ENOSYS)
81-
} else {
82-
true
83-
};
84-
AVAILABLE.store(available, Ordering::Relaxed);
85-
});
86-
87-
AVAILABLE.load(Ordering::Relaxed)
71+
true
8872
}
8973

90-
#[cfg(not(any(target_os = "linux", target_os = "android")))]
91-
fn is_getrandom_available() -> bool { false }
92-
9374
pub fn fill_bytes(v: &mut [u8]) {
9475
// getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
9576
// meaning it would have blocked because the non-blocking pool (urandom)
96-
// has not initialized in the kernel yet due to a lack of entropy the
77+
// has not initialized in the kernel yet due to a lack of entropy. The
9778
// fallback we do here is to avoid blocking applications which could
9879
// depend on this call without ever knowing they do and don't have a
99-
// work around. The PRNG of /dev/urandom will still be used but not
100-
// over a completely full entropy pool
101-
if is_getrandom_available() && getrandom_fill_bytes(v) {
102-
return
80+
// work around. The PRNG of /dev/urandom will still be used but over a
81+
// possibly predictable entropy pool.
82+
if getrandom_fill_bytes(v) {
83+
return;
10384
}
10485

105-
let mut file = File::open("/dev/urandom")
106-
.expect("failed to open /dev/urandom");
107-
file.read_exact(v).expect("failed to read /dev/urandom");
86+
// getrandom failed because it is permanently or temporarily (because
87+
// of missing entropy) unavailable. Open /dev/urandom, read from it,
88+
// and close it again.
89+
let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
90+
file.read_exact(v).expect("failed to read /dev/urandom")
10891
}
10992
}
11093

0 commit comments

Comments
 (0)