diff --git a/src/cloudabi.rs b/src/cloudabi.rs index e87359ee..414429d9 100644 --- a/src/cloudabi.rs +++ b/src/cloudabi.rs @@ -8,7 +8,6 @@ //! Implementation for CloudABI use crate::Error; -use core::num::NonZeroU32; extern "C" { fn cloudabi_sys_random_get(buf: *mut u8, buf_len: usize) -> u16; @@ -16,15 +15,15 @@ extern "C" { pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { let errno = unsafe { cloudabi_sys_random_get(dest.as_mut_ptr(), dest.len()) }; - if let Some(code) = NonZeroU32::new(errno as u32) { - error!("cloudabi_sys_random_get failed with code {}", code); - Err(Error::from(code)) + if errno != 0 { + error!("cloudabi_sys_random_get: failed with {}", errno); + Err(Error::from_os_error(code as i32)) } else { Ok(()) // Zero means success for CloudABI } } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/dummy.rs b/src/dummy.rs index b53f66c1..fe321ae9 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -9,14 +9,12 @@ //! A dummy implementation for unsupported targets which always returns //! `Err(Error::UNAVAILABLE)` use crate::Error; -use core::num::NonZeroU32; pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> { - error!("no support for this platform"); - Err(Error::UNAVAILABLE) + Err(Error::from_custom_error(0)) } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None +pub fn custom_description(custom: u16) -> Option<&'static str> { + Some("getrandom: this target is not supported") } diff --git a/src/error.rs b/src/error.rs index a83ea111..0d95f387 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,78 +5,112 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::convert::From; use core::fmt; use core::num::NonZeroU32; -// A randomly-chosen 24-bit prefix for our codes -pub(crate) const CODE_PREFIX: u32 = 0x57f4c500; -const CODE_UNKNOWN: u32 = CODE_PREFIX | 0x00; -const CODE_UNAVAILABLE: u32 = CODE_PREFIX | 0x01; - /// The error type. /// /// This type is small and no-std compatible. #[derive(Copy, Clone, Eq, PartialEq)] -pub struct Error(pub(crate) NonZeroU32); +pub struct Error(NonZeroU32); +// TODO simplify impls with try_from when version >= 1.34 impl Error { /// An unknown error. - pub const UNKNOWN: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNKNOWN) }); + pub(crate) const UNKNOWN: Error = Self(unsafe { NonZeroU32::new_unchecked(u32::max_value()) }); + const CUSTOM_START: u32 = 1 << 31; - /// No generator is available. - pub const UNAVAILABLE: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNAVAILABLE) }); + pub(crate) fn from_os_error(errno: i32) -> Self { + if errno > 0 { + Self(NonZeroU32::new(errno as u32).unwrap()) + } else { + Self::UNKNOWN + } + } - /// Extract the error code. - /// - /// This may equal one of the codes defined in this library or may be a - /// system error code. - /// - /// One may attempt to format this error via the `Display` implementation. - pub fn code(&self) -> NonZeroU32 { - self.0 + pub(crate) fn from_custom_error(custom: u16) -> Self { + Self(NonZeroU32::new(custom as u32 + Self::CUSTOM_START).unwrap()) } - pub(crate) fn msg(&self) -> Option<&'static str> { - if let Some(msg) = crate::imp::error_msg_inner(self.0) { - Some(msg) + /// Extract the raw OS error code (if this error came from the OS) + /// + /// This method is identical to [`std::io::Error::raw_os_error()`], except + /// that it works in `no_std` contexts. If this method returns None, the + /// error value can stilly be formatted via the `Diplay` implementation. + pub fn raw_os_error(&self) -> Option { + if self.0.get() < Self::CUSTOM_START { + Some(self.0.get() as i32) } else { - match *self { - Error::UNKNOWN => Some("getrandom: unknown error"), - Error::UNAVAILABLE => Some("getrandom: unavailable"), - _ => None, - } + None } } -} -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match self.msg() { - Some(msg) => write!(f, "Error(\"{}\")", msg), - None => write!(f, "Error(0x{:08X})", self.0), + pub fn custom_code(&self) -> Option { + let custom = self.0.get().checked_sub(Self::CUSTOM_START)?; + if custom <= u16::max_value() as u32 { + Some(custom as u16) + } else { + None } } } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match self.msg() { - Some(msg) => write!(f, "{}", msg), - None => write!(f, "getrandom: unknown code 0x{:08X}", self.0), - } +#[cfg(any(unix, target_os = "redox"))] +fn os_err_desc(errno: i32, buf: &mut [u8]) -> Option<&str> { + let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; + if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { + return None; } + + // Take up to trailing null byte + let idx = buf.iter().position(|&b| b == 0).unwrap_or(buf.len()); + core::str::from_utf8(&buf[..idx]).ok() +} + +#[cfg(not(any(unix, target_os = "redox")))] +fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> { + None } -impl From for Error { - fn from(code: NonZeroU32) -> Self { - Error(code) +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut dbg = f.debug_struct("Error"); + if let Some(errno) = self.raw_os_error() { + dbg.field("os_error", &errno); + let mut buf = [0u8; 128]; + if let Some(desc) = os_err_desc(errno, &mut buf) { + dbg.field("description", &desc); + } + } else if let Some(custom) = self.custom_code() { + dbg.field("custom_error", &custom); + if let Some(desc) = crate::imp::custom_description(custom) { + dbg.field("description", &desc); + } + } else { + dbg.field("unknown_error", &self.0); + } + dbg.finish() } } -impl From<&Error> for Error { - fn from(error: &Error) -> Self { - *error +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(errno) = self.raw_os_error() { + let mut buf = [0u8; 128]; + if let Some(desc) = os_err_desc(errno, &mut buf) { + f.write_str(desc) + } else { + write!(f, "OS Error: {}", errno) + } + } else if let Some(custom) = self.custom_code() { + if let Some(desc) = crate::imp::custom_description(custom) { + f.write_str(desc) + } else { + write!(f, "Custom Error: {}", custom) + } + } else { + write!(f, "Unknown Error: {}", self.0) + } } } diff --git a/src/error_impls.rs b/src/error_impls.rs index 6bd2c69a..d8b6e95f 100644 --- a/src/error_impls.rs +++ b/src/error_impls.rs @@ -9,24 +9,22 @@ extern crate std; use crate::error::Error; use core::convert::From; -use core::num::NonZeroU32; use std::{error, io}; impl From for Error { fn from(err: io::Error) -> Self { - err.raw_os_error() - .and_then(|code| NonZeroU32::new(code as u32)) - .map(|code| Error(code)) - // in practice this should never happen - .unwrap_or(Error::UNKNOWN) + match err.raw_os_error() { + Some(errno) => Error::from_os_error(errno), + None => Error::UNKNOWN, + } } } impl From for io::Error { fn from(err: Error) -> Self { - match err.msg() { - Some(msg) => io::Error::new(io::ErrorKind::Other, msg), - None => io::Error::from_raw_os_error(err.0.get() as i32), + match err.raw_os_error() { + Some(errno) => io::Error::from_raw_os_error(errno), + None => io::Error::new(io::ErrorKind::Other, err), } } } diff --git a/src/freebsd.rs b/src/freebsd.rs index 3a8ab609..5df97374 100644 --- a/src/freebsd.rs +++ b/src/freebsd.rs @@ -7,14 +7,11 @@ // except according to those terms. //! Implementation for FreeBSD -extern crate std; - +use crate::util_libc::fill_exact; use crate::Error; -use core::num::NonZeroU32; use core::ptr; -use std::io; -fn kern_arnd(buf: &mut [u8]) -> Result { +fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND]; let mut len = buf.len(); let ret = unsafe { @@ -28,21 +25,17 @@ fn kern_arnd(buf: &mut [u8]) -> Result { ) }; if ret == -1 { - error!("freebsd: kern.arandom syscall failed"); - return Err(io::Error::last_os_error().into()); + -1 + } else { + len as libc::ssize_t } - Ok(len) } pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - let mut start = 0; - while start < dest.len() { - start += kern_arnd(&mut dest[start..])?; - } - Ok(()) + fill_exact(dest, kern_arnd) } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/fuchsia.rs b/src/fuchsia.rs index a9f53888..137f3860 100644 --- a/src/fuchsia.rs +++ b/src/fuchsia.rs @@ -8,7 +8,6 @@ //! Implementation for Fuchsia Zircon use crate::Error; -use core::num::NonZeroU32; #[link(name = "zircon")] extern "C" { @@ -21,6 +20,6 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/ios.rs b/src/ios.rs index 97d68979..8b5b95ef 100644 --- a/src/ios.rs +++ b/src/ios.rs @@ -7,10 +7,9 @@ // except according to those terms. //! Implementation for iOS -extern crate std; - use crate::Error; -use core::num::NonZeroU32; + +const CALL_FAILED: u16 = 0; // TODO: Make extern once extern_types feature is stabilized. See: // https://github.com/rust-lang/rust/issues/43467 @@ -27,14 +26,16 @@ extern "C" { pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) }; if ret == -1 { - error!("SecRandomCopyBytes call failed"); - Err(Error::UNKNOWN) + Err(Error::from_custom_error(CALL_FAILED)) } else { Ok(()) } } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None +pub fn custom_description(_: u16) -> Option<&'static str> { + match custom { + CALL_FAILED => Some("SecRandomCopyBytes: call failed"), + _ => None, + } } diff --git a/src/lib.rs b/src/lib.rs index 16cbac9b..bc2fbb61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,12 +146,26 @@ cfg_if! { #[cfg(feature = "std")] extern crate std; +#[allow(dead_code)] mod error; pub use crate::error::Error; #[allow(dead_code)] mod util; -#[cfg(any(unix, target_os = "redox"))] +#[cfg(any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "android", + target_os = "linux", + target_os = "emscripten", + target_os = "solaris", + target_os = "illumos", + target_os = "macos", + target_os = "freebsd", + target_os = "haiku", + target_os = "redox", + target_os = "dragonfly", +))] #[allow(dead_code)] mod util_libc; @@ -172,7 +186,7 @@ mod use_file; // System-specific implementations. // -// These should all provide getrandom_inner with the same signature as getrandom. +// These should all provide getrandom_inner and custom_description. cfg_if! { if #[cfg(target_os = "android")] { #[path = "linux_android.rs"] mod imp; diff --git a/src/linux_android.rs b/src/linux_android.rs index d7c046da..7a06f33a 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -7,52 +7,35 @@ // except according to those terms. //! Implementation for Linux / Android -extern crate std; - use crate::util::LazyBool; +use crate::util_libc::{fill_exact, last_os_error}; use crate::{use_file, Error}; -use core::num::NonZeroU32; -use std::io; - -fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result { - 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> { static HAS_GETRANDOM: LazyBool = LazyBool::new(); if HAS_GETRANDOM.unsync_init(is_getrandom_available) { - let mut start = 0; - while start < dest.len() { - start += syscall_getrandom(&mut dest[start..], true)?; - } - Ok(()) + fill_exact(dest, |buf| unsafe { + libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t + }) } else { 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 { libc::syscall(libc::SYS_getrandom, 0, 0, libc::GRND_NONBLOCK) }; + if res < 0 { + match 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> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/macos.rs b/src/macos.rs index 84d95565..91b8610c 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -7,13 +7,9 @@ // except according to those terms. //! Implementation for macOS -extern crate std; - -use crate::util_libc::Weak; +use crate::util_libc::{last_os_error, Weak}; use crate::{use_file, Error}; use core::mem; -use core::num::NonZeroU32; -use std::io; type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; @@ -24,8 +20,9 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { for chunk in dest.chunks_mut(256) { let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) }; if ret != 0 { - error!("getentropy syscall failed with ret={}", ret); - return Err(io::Error::last_os_error().into()); + let err = last_os_error(); + error!("getentropy syscall failed"); + return Err(err); } } Ok(()) @@ -37,6 +34,6 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/openbsd_bitrig.rs b/src/openbsd_bitrig.rs index 24e5c191..3b45f9e3 100644 --- a/src/openbsd_bitrig.rs +++ b/src/openbsd_bitrig.rs @@ -7,24 +7,22 @@ // except according to those terms. //! Implementation for OpenBSD / Bitrig -extern crate std; - +use crate::util_libc::last_os_error; use crate::Error; -use core::num::NonZeroU32; -use std::io; pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { for chunk in dest.chunks_mut(256) { let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) }; if ret == -1 { + let err = last_os_error(); error!("libc::getentropy call failed"); - return Err(io::Error::last_os_error().into()); + return Err(err); } } Ok(()) } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/rdrand.rs b/src/rdrand.rs index 5a4bb286..09d7b26e 100644 --- a/src/rdrand.rs +++ b/src/rdrand.rs @@ -12,7 +12,10 @@ use crate::util::LazyBool; use crate::Error; use core::arch::x86_64::_rdrand64_step; use core::mem; -use core::num::NonZeroU32; + +const NO_RDRAND: u16 = 0; +const BAD_RDRAND: u16 = 1; +const FAILED_RDRAND: u16 = 2; // Recommendation from "IntelĀ® Digital Random Number Generator (DRNG) Software // Implementation Guide" - Section 5.2.1 and "IntelĀ® 64 and IA-32 Architectures @@ -22,6 +25,7 @@ const WORD_SIZE: usize = mem::size_of::(); #[target_feature(enable = "rdrand")] unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { + let mut is_bad = false; for _ in 0..RETRY_LIMIT { let mut el = mem::uninitialized(); if _rdrand64_step(&mut el) == 1 { @@ -33,12 +37,15 @@ unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { if el != 0 && el != !0 { return Ok(el.to_ne_bytes()); } - error!("RDRAND returned {:X}, CPU RNG may be broken", el); + is_bad == true; // Keep looping in case this was a false positive. } } - error!("RDRAND failed, CPU issue likely"); - Err(Error::UNKNOWN) + Err(Error::from_custom_error(if is_bad { + BAD_RDRAND + } else { + FAILED_RDRAND + })) } // "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653. @@ -65,7 +72,7 @@ fn is_rdrand_supported() -> bool { pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { if !is_rdrand_supported() { - return Err(Error::UNAVAILABLE); + return Err(Error::from_custom_error(NO_RDRAND)); } // SAFETY: After this point, rdrand is supported, so calling the rdrand @@ -91,6 +98,11 @@ unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> { } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None +pub fn error_msg_inner(custom: u16) -> Option<&'static str> { + match custom { + NO_RDRAND => Some("RDRAND: instruction not supported"), + BAD_RDRAND => Some("RDRAND: firmware error: returned insecure values"), + FAILED_RDRAND => Some("RDRAND: CPU error: instruction failed"), + _ => None, + } } diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs index 9d629e40..351e495a 100644 --- a/src/solaris_illumos.rs +++ b/src/solaris_illumos.rs @@ -17,13 +17,9 @@ //! To make sure we can compile on both Solaris and its derivatives, as well as //! function, we check for the existance of getrandom(2) in libc by calling //! libc::dlsym. -extern crate std; - use crate::util_libc::Weak; use crate::{use_file, Error}; use core::mem; -use core::num::NonZeroU32; -use std::io; #[cfg(target_os = "illumos")] type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; @@ -37,11 +33,9 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { // 256 bytes is the lowest common denominator across all the Solaris // derived platforms for atomically obtaining random data. for chunk in dest.chunks_mut(256) { - let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len(), 0) }; - if ret != chunk.len() as _ { - error!("getrandom syscall failed with ret={}", ret); - return Err(io::Error::last_os_error().into()); - } + fill_exact(chunk, |buf| unsafe { + func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t + })? } Ok(()) } else { @@ -50,6 +44,6 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/use_file.rs b/src/use_file.rs index adc1b3cd..f0984b85 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -9,15 +9,11 @@ //! Implementations that just need to read from a file extern crate std; -use crate::util_libc::LazyFd; +use crate::util_libc::{last_os_error, LazyFd}; use crate::Error; use core::mem::ManuallyDrop; -use core::num::NonZeroU32; use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd}; -use std::{ - fs::File, - io::{self, Read}, -}; +use std::{fs::File, io::Read}; #[cfg(target_os = "redox")] const FILE_PATH: &str = "rand:"; @@ -35,7 +31,7 @@ const FILE_PATH: &str = "/dev/random"; pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { static FD: LazyFd = LazyFd::new(); - let fd = FD.init(init_file).ok_or(io::Error::last_os_error())?; + let fd = FD.init(init_file).ok_or(last_os_error())?; let file = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) }); let mut file_ref: &File = &file; @@ -62,7 +58,6 @@ fn init_file() -> Option { } #[inline(always)] -#[allow(dead_code)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/util_libc.rs b/src/util_libc.rs index 5554079b..6d8b39cc 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -5,10 +5,61 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. - +use crate::error::Error; use crate::util::LazyUsize; use core::ptr::NonNull; +// Taken from libstd. TODO: replace with use statements when all are in libc. +cfg_if! { + if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] { + use libc::__errno as errno_location; + } else if #[cfg(any(target_os = "linux", target_os = "emscripten"))] { + use libc::__errno_location as errno_location; + } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + use libc::___errno as errno_location; + } else if #[cfg(any(target_os = "macos", target_os = "freebsd"))] { + use libc::__error as errno_location; + } else if #[cfg(target_os = "haiku")] { + extern { + #[link_name = "_errnop"] + fn errno_location() -> *mut libc::c_int; + } + } else if #[cfg(any(target_os = "redox"))] { + extern { + #[link_name = "__errno_location"] + fn errno_location() -> *mut libc::c_int; + } + } else if #[cfg(target_os = "dragonfly")] { + extern { + #[thread_local] + static errno: libc::c_int; + } + unsafe fn errno_location() -> &libc::c_int { + &errno + } + } +} + +pub fn last_os_error() -> Error { + Error::from_os_error(unsafe { *errno_location() }) +} + +pub fn fill_exact(mut buf: &mut [u8], f: impl Fn(&mut [u8]) -> libc::ssize_t) -> Result<(), Error> { + while !buf.is_empty() { + let res = f(buf); + if res < 0 { + let errno = unsafe { *errno_location() }; + // We should try again if the call was interrupted. + if errno != libc::EINTR { + return Err(Error::from_os_error(errno)); + } + } else { + buf = &mut buf[(res as usize)..]; + } + } + Ok(()) +} + // A "weak" binding to a C function that may or may not be present at runtime. // Used for supporting newer OS features while still building on older systems. // F must be a function pointer of type `unsafe extern "C" fn`. Based off of the diff --git a/src/wasi.rs b/src/wasi.rs index 10b91788..36749127 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -8,20 +8,19 @@ //! Implementation for WASI use crate::Error; -use core::num::NonZeroU32; pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { let ret = unsafe { libc::__wasi_random_get(dest.as_mut_ptr() as *mut libc::c_void, dest.len()) }; - if let Some(code) = NonZeroU32::new(ret as u32) { - error!("WASI: __wasi_random_get failed with return value {}", code); - Err(Error::from(code)) + if ret != 0 { + error!("WASI: __wasi_random_get: failed with {}", ret); + Err(Error::from_os_error(ret)) } else { Ok(()) // Zero means success for WASI } } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { +pub fn custom_description(_: u16) -> Option<&'static str> { None } diff --git a/src/wasm32_bindgen.rs b/src/wasm32_bindgen.rs index 08df426b..1392328a 100644 --- a/src/wasm32_bindgen.rs +++ b/src/wasm32_bindgen.rs @@ -11,16 +11,14 @@ extern crate std; use core::cell::RefCell; use core::mem; -use core::num::NonZeroU32; use std::thread_local; use wasm_bindgen::prelude::*; -use crate::error::CODE_PREFIX; use crate::Error; -const CODE_CRYPTO_UNDEF: u32 = CODE_PREFIX | 0x80; -const CODE_GRV_UNDEF: u32 = CODE_PREFIX | 0x81; +const CRYPTO_UNDEF: u16 = 0; +const GRV_UNDEF: u16 = 1; #[derive(Clone, Debug)] enum RngSource { @@ -83,17 +81,13 @@ fn getrandom_init() -> Result { // we're in an older web browser and the OS RNG isn't available. let crypto = this.crypto(); if crypto.is_undefined() { - return Err(Error::from(unsafe { - NonZeroU32::new_unchecked(CODE_CRYPTO_UNDEF) - })); + return Err(Error::from_custom_error(CRYPTO_UNDEF)); } // Test if `crypto.getRandomValues` is undefined as well let crypto: BrowserCrypto = crypto.into(); if crypto.get_random_values_fn().is_undefined() { - return Err(Error::from(unsafe { - NonZeroU32::new_unchecked(CODE_GRV_UNDEF) - })); + return Err(Error::from_custom_error(GRV_UNDEF)); } // Ok! `self.crypto.getRandomValues` is a defined value, so let's @@ -102,10 +96,10 @@ fn getrandom_init() -> Result { } #[inline(always)] -pub fn error_msg_inner(n: NonZeroU32) -> Option<&'static str> { - match n.get() { - CODE_CRYPTO_UNDEF => Some("getrandom: self.crypto is undefined"), - CODE_GRV_UNDEF => Some("crypto.getRandomValues is undefined"), +pub fn custom_description(custom: u16) -> Option<&'static str> { + match custom { + CRYPTO_UNDEF => Some("getrandom: self.crypto is undefined"), + GRV_UNDEF => Some("crypto.getRandomValues is undefined"), _ => None, } } diff --git a/src/wasm32_stdweb.rs b/src/wasm32_stdweb.rs index 32a5686c..939dc079 100644 --- a/src/wasm32_stdweb.rs +++ b/src/wasm32_stdweb.rs @@ -8,7 +8,6 @@ //! Implementation for WASM via stdweb use core::mem; -use core::num::NonZeroU32; use stdweb::unstable::TryInto; use stdweb::web::error::Error as WebError; @@ -17,6 +16,9 @@ use stdweb::{_js_impl, js}; use crate::Error; use std::sync::Once; +const NO_RNG_METHOD: u16 = 0; +const METHOD_FAILED: u16 = 1; + #[derive(Clone, Copy, Debug)] enum RngSource { Browser, @@ -69,7 +71,7 @@ fn getrandom_init() -> Result { } else { let err: WebError = js! { return @{ result }.error }.try_into().unwrap(); error!("getrandom unavailable: {}", err); - Err(Error::UNAVAILABLE) + Err(Error::from_custom_error(NO_RNG_METHOD)) } } @@ -105,13 +107,17 @@ fn getrandom_fill(source: RngSource, dest: &mut [u8]) -> Result<(), Error> { if js! { return @{ result.as_ref() }.success } != true { let err: WebError = js! { return @{ result }.error }.try_into().unwrap(); error!("getrandom failed: {}", err); - return Err(Error::UNKNOWN); + return Err(Error::from_custom_error(METHOD_FAILED)); } } Ok(()) } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None +pub fn custom_description(custom: u16) -> Option<&'static str> { + match custom { + NO_RNG_METHOD => Some("getrandom: no randomness source available"), + METHOD_FAILED => Some("getrandom: stdweb call failed"), + _ => None, + } } diff --git a/src/windows.rs b/src/windows.rs index c52e9f10..759a415d 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -7,10 +7,9 @@ // except according to those terms. //! Implementation for Windows -extern crate std; - use crate::Error; -use core::num::NonZeroU32; + +const CALL_FAILED: u16 = 0; extern "system" { #[link_name = "SystemFunction036"] @@ -22,14 +21,16 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { for chunk in dest.chunks_mut(u32::max_value() as usize) { let ret = unsafe { RtlGenRandom(chunk.as_mut_ptr(), chunk.len() as u32) }; if ret == 0 { - error!("RtlGenRandom call failed"); - return Err(Error::UNKNOWN); + return Err(Error::from_custom_error(CALL_FAILED)); } } Ok(()) } #[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None +pub fn custom_description(custom: u16) -> Option<&'static str> { + match custom { + CALL_FAILED => Some("RtlGenRandom: call failed"), + _ => None, + } }