Skip to content

Commit 5a565b6

Browse files
committed
Linux/Android: Read a byte from /dev/random instead of polling it.
See the added comment for details.
1 parent 40e873d commit 5a565b6

File tree

1 file changed

+21
-20
lines changed

1 file changed

+21
-20
lines changed

src/use_file.rs

+21-20
Original file line numberDiff line numberDiff line change
@@ -68,30 +68,31 @@ fn get_rng_fd() -> Result<libc::c_int, Error> {
6868
// Succeeds once /dev/urandom is safe to read from
6969
#[cfg(any(target_os = "android", target_os = "linux"))]
7070
fn wait_until_rng_ready() -> Result<(), Error> {
71-
// Poll /dev/random to make sure it is ok to read from /dev/urandom.
71+
// Read a byte from /dev/random to make sure it is ok to read from
72+
// /dev/urandom. reading a byte instead of polling is more compatible with
73+
// sandboxes that disallow `poll()` but which allow reading /dev/random,
74+
// e.g. sandboxes that assume that `poll()` is for network I/O. This way,
75+
// fewer applications will have to insert pre-sandbox-initialization logic.
76+
// Often (blocking) file I/O is not allowed in such early phases of an
77+
// application for performance and/or security reasons. Also it is hard to
78+
// write a sandbox policy to support `libc::poll()` because it may be
79+
// invoked as `SYS_POLL`, `SYS_PPOLL`, or even `SYS_SELECT`, depending on
80+
// the libc implementation (e.g. glibc vs musl), libc version, potentially
81+
// the kernel version at runtime, and/or the target architecture.
82+
//
83+
// BoringSSL and libstd don't try to protect against insecure output from
84+
// `/dev/urandom'; they don't open `/dev/random` at all.
85+
//
86+
// OpenSSL uses `libc::select()` unless the `dev/random` file descriptor
87+
// is too large; if it is too large then it does what we do here.
7288
let fd = open_readonly(b"/dev/random\0")?;
73-
let mut pfd = libc::pollfd {
74-
fd,
75-
events: libc::POLLIN,
76-
revents: 0,
77-
};
7889
let _guard = DropGuard(|| unsafe {
7990
libc::close(fd);
8091
});
81-
82-
loop {
83-
// A negative timeout means an infinite timeout.
84-
let res = unsafe { libc::poll(&mut pfd, 1, -1) };
85-
if res >= 0 {
86-
debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout.
87-
return Ok(());
88-
}
89-
let err = crate::util_libc::last_os_error();
90-
match err.raw_os_error() {
91-
Some(libc::EINTR) | Some(libc::EAGAIN) => continue,
92-
_ => return Err(err),
93-
}
94-
}
92+
let mut dummy: [MaybeUninit<u8>; 1] = [MaybeUninit::uninit()];
93+
sys_fill_exact(&mut dummy, |buf| unsafe {
94+
libc::read(fd, buf.as_mut_ptr().cast::<c_void>(), buf.len())
95+
})
9596
}
9697

9798
struct Mutex(UnsafeCell<libc::pthread_mutex_t>);

0 commit comments

Comments
 (0)