Skip to content

Commit

Permalink
Explicitly specify types to arguments of 'libc::syscall' (rust-random#74
Browse files Browse the repository at this point in the history
)

The 'libc::syscall' function uses varargs - as a result, its arguments
are completely untyped. THe user must ensure that it is called with the
proper types for the targeted syscall - otherwise, the calling
convention might cause arguments to be put into the wrong registers.

This commit explicitly casts the arguments to 'libc::syscall' to the
proper type for the 'getrandom' syscall. This ensures that the correct
types for the target platform will always be used, instead of relying on
the types used happening to match those required by the target platform.

This particular commit is a backport of
6716ad0, with the addition of
`sys_fill_exact` from master (originally committed in
65660e0) to make the backport more
obviously correct.

# Conflicts:
#	src/linux_android.rs
  • Loading branch information
Aaron1011 authored and froydnj committed Aug 3, 2020
1 parent e02e946 commit df0b03f
Showing 1 changed file with 44 additions and 24 deletions.
68 changes: 44 additions & 24 deletions src/linux_android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,48 +14,68 @@ use core::num::NonZeroU32;
use lazy_static::lazy_static;
use std::io;

fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result<usize, io::Error> {
let flags = if block { 0 } else { libc::GRND_NONBLOCK };
let ret = unsafe { libc::syscall(libc::SYS_getrandom, dest.as_mut_ptr(), dest.len(), flags) };
if ret < 0 {
let err = io::Error::last_os_error();
if err.raw_os_error() == Some(libc::EINTR) {
return Ok(0); // Call was interrupted, try again
}
error!("Linux getrandom syscall failed with return value {}", ret);
return Err(err);
}
Ok(ret as usize)
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
lazy_static! {
static ref HAS_GETRANDOM: bool = is_getrandom_available();
}
match *HAS_GETRANDOM {
true => {
let mut start = 0;
while start < dest.len() {
start += syscall_getrandom(&mut dest[start..], true)?;
}
Ok(())
}
sys_fill_exact(dest, |buf| unsafe {
getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
})
},
false => use_file::getrandom_inner(dest),
}
}

fn is_getrandom_available() -> bool {
match syscall_getrandom(&mut [], false) {
Err(err) => match err.raw_os_error() {
let res = unsafe { getrandom(core::ptr::null_mut(), 0, libc::GRND_NONBLOCK) };
if res < 0 {
match io::Error::last_os_error().raw_os_error() {
Some(libc::ENOSYS) => false, // No kernel support
Some(libc::EPERM) => false, // Blocked by seccomp
_ => true,
},
Ok(_) => true,
}
} else {
true
}
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
None
}

// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function:
// - should return -1 and set errno on failure
// - should return the number of bytes written on success
//
// From src/util_libc.rs 65660e00
fn sys_fill_exact(
mut buf: &mut [u8],
sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t,
) -> Result<(), Error> {
while !buf.is_empty() {
let res = sys_fill(buf);
if res < 0 {
let err = io::Error::last_os_error();
// We should try again if the call was interrupted.
if err.raw_os_error() != Some(libc::EINTR) {
return Err(err.into());
}
} else {
// We don't check for EOF (ret = 0) as the data we are reading
// should be an infinite stream of random bytes.
buf = &mut buf[(res as usize)..];
}
}
Ok(())
}

unsafe fn getrandom(
buf: *mut libc::c_void,
buflen: libc::size_t,
flags: libc::c_uint,
) -> libc::ssize_t {
libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as libc::ssize_t
}

0 comments on commit df0b03f

Please sign in to comment.