Skip to content

Commit

Permalink
Improve robustness of the Hermit backend and sys_fill_exact (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed Dec 2, 2023
1 parent 169944f commit 7249e6e
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 18 deletions.
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ impl Error {
pub const UNSUPPORTED: Error = internal_error(0);
/// The platform-specific `errno` returned a non-positive value.
pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
/// Encountered an unexpected situation which should not happen in practice.
pub const UNEXPECTED: Error = internal_error(2);
/// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed.
pub const IOS_SEC_RANDOM: Error = internal_error(3);
/// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
Expand Down Expand Up @@ -164,6 +166,7 @@ fn internal_desc(error: Error) -> Option<&'static str> {
match error {
Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
Error::UNEXPECTED => Some("unexpected situation"),
Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
Expand Down
23 changes: 15 additions & 8 deletions src/hermit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use crate::Error;
use core::{cmp::min, mem::MaybeUninit, num::NonZeroU32};
use core::{mem::MaybeUninit, num::NonZeroU32};

/// Minimum return value which we should get from syscalls in practice,
/// because Hermit uses positive `i32`s for error codes:
/// https://github.com/hermitcore/libhermit-rs/blob/main/src/errno.rs
const MIN_RET_CODE: isize = -(i32::MAX as isize);

extern "C" {
fn sys_read_entropy(buffer: *mut u8, length: usize, flags: u32) -> isize;
Expand All @@ -8,14 +13,16 @@ extern "C" {
pub fn getrandom_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
while !dest.is_empty() {
let res = unsafe { sys_read_entropy(dest.as_mut_ptr() as *mut u8, dest.len(), 0) };
if res < 0 {
// SAFETY: all Hermit error codes use i32 under the hood:
// https://github.com/hermitcore/libhermit-rs/blob/master/src/errno.rs
let code = unsafe { NonZeroU32::new_unchecked((-res) as u32) };
return Err(code.into());
// Positive `isize`s can be safely casted to `usize`
if res > 0 && (res as usize) <= dest.len() {
dest = &mut dest[res as usize..];
} else {
let err = match res {
MIN_RET_CODE..=-1 => NonZeroU32::new(-res as u32).unwrap().into(),
_ => Error::UNEXPECTED,
};
return Err(err);
}
let len = min(res as usize, dest.len());
dest = &mut dest[len..];
}
Ok(())
}
21 changes: 11 additions & 10 deletions src/util_libc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#![allow(dead_code)]
use crate::Error;
use core::{
cmp::min,
mem::MaybeUninit,
num::NonZeroU32,
ptr::NonNull,
Expand Down Expand Up @@ -70,17 +69,19 @@ pub fn sys_fill_exact(
) -> Result<(), Error> {
while !buf.is_empty() {
let res = sys_fill(buf);
if res < 0 {
let err = last_os_error();
// We should try again if the call was interrupted.
if err.raw_os_error() != Some(libc::EINTR) {
return Err(err);
match res {
res if res > 0 => buf = buf.get_mut(res as usize..).ok_or(Error::UNEXPECTED)?,
-1 => {
let err = last_os_error();
// We should try again if the call was interrupted.
if err.raw_os_error() != Some(libc::EINTR) {
return Err(err);
}
}
} else {
// We don't check for EOF (ret = 0) as the data we are reading
// Negative return codes not equal to -1 should be impossible.
// EOF (ret = 0) should be impossible, as the data we are reading
// should be an infinite stream of random bytes.
let len = min(res as usize, buf.len());
buf = &mut buf[len..];
_ => return Err(Error::UNEXPECTED),
}
}
Ok(())
Expand Down

0 comments on commit 7249e6e

Please sign in to comment.