getrandom/utils/
sys_fill_exact.rs

1use crate::Error;
2use core::mem::MaybeUninit;
3
4mod get_errno;
5mod sanitizer;
6
7pub(crate) use get_errno::get_errno;
8
9/// Fill a buffer by repeatedly invoking `sys_fill`.
10///
11/// The `sys_fill` function:
12///   - should return -1 and set errno on failure
13///   - should return the number of bytes written on success
14pub(crate) fn sys_fill_exact(
15    mut buf: &mut [MaybeUninit<u8>],
16    sys_fill: impl Fn(&mut [MaybeUninit<u8>]) -> libc::ssize_t,
17) -> Result<(), Error> {
18    while !buf.is_empty() {
19        let res = sys_fill(buf);
20        match res {
21            res if res > 0 => {
22                let len = usize::try_from(res).map_err(|_| Error::UNEXPECTED)?;
23                let (l, r) = buf.split_at_mut_checked(len).ok_or(Error::UNEXPECTED)?;
24                unsafe { sanitizer::unpoison(l) };
25                buf = r;
26            }
27            -1 => {
28                let errno = get_errno();
29                // We should try again if the call was interrupted.
30                if errno != libc::EINTR {
31                    return Err(Error::from_errno(errno));
32                }
33            }
34            // Negative return codes not equal to -1 should be impossible.
35            // EOF (ret = 0) should be impossible, as the data we are reading
36            // should be an infinite stream of random bytes.
37            _ => return Err(Error::UNEXPECTED),
38        }
39    }
40    Ok(())
41}