From a5847c29e612763e613b8afa3f89ff260c6094af Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sat, 6 Jul 2019 23:36:27 -0700 Subject: [PATCH 1/7] Remove std dependancy for error handling --- Cargo.toml | 2 +- src/cloudabi.rs | 12 +-- src/dummy.rs | 14 +--- src/error.rs | 162 +++++++++++++++++++++++++++++++---------- src/error_impls.rs | 22 +++--- src/freebsd.rs | 6 -- src/fuchsia.rs | 6 -- src/ios.rs | 13 +--- src/lib.rs | 22 +----- src/linux_android.rs | 13 +--- src/macos.rs | 16 +--- src/openbsd.rs | 13 +--- src/rdrand.rs | 12 +-- src/solaris_illumos.rs | 6 -- src/use_file.rs | 16 +--- src/util_libc.rs | 25 +++++-- src/wasi.rs | 12 +-- src/wasm32_bindgen.rs | 23 +----- src/wasm32_stdweb.rs | 13 +--- src/windows.rs | 13 +--- 20 files changed, 193 insertions(+), 228 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b813cc1..c67beac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ log = { version = "0.4", optional = true } cfg-if = "0.1" [target.'cfg(any(unix, target_os = "redox", target_os = "wasi"))'.dependencies] -libc = "0.2.54" +libc = "0.2.60" [target.wasm32-unknown-unknown.dependencies] wasm-bindgen = { version = "0.2.29", optional = true } diff --git a/src/cloudabi.rs b/src/cloudabi.rs index e87359ee..e647a9f4 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,10 @@ 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(errno.into())) } else { Ok(()) // Zero means success for CloudABI } } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/dummy.rs b/src/dummy.rs index b53f66c1..1f5f6ddc 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -6,17 +6,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A dummy implementation for unsupported targets which always returns -//! `Err(Error::UNAVAILABLE)` -use crate::Error; -use core::num::NonZeroU32; +//! A dummy implementation for unsupported targets which always fails +use crate::{error::UNSUPPORTED, Error}; pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> { - error!("no support for this platform"); - Err(Error::UNAVAILABLE) -} - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None + Err(Error::internal(UNSUPPORTED)) } diff --git a/src/error.rs b/src/error.rs index a83ea111..ad7ec9d2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,78 +5,166 @@ // , 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. +/// A small and `no_std` compatible error type. /// -/// This type is small and no-std compatible. +/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and +/// if so, which error code the OS gave the application. If such an error is +/// encountered, please consult with your system documentation. #[derive(Copy, Clone, Eq, PartialEq)] -pub struct Error(pub(crate) NonZeroU32); +pub struct Error(NonZeroU32); + +// This NonZeroU32 in Error has enough room for two types of errors: +// - OS Errors: in range [1, 1 << 31) (i.e. positive i32 values) +// - Custom Errors: in range [1 << 31, 1 << 32) (in blocks of 1 << 16) +const CUSTOM_START: u32 = 1 << 31; +const BLOCK_SIZE: u32 = 1 << 16; impl Error { - /// An unknown error. - pub const UNKNOWN: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNKNOWN) }); + /// Create a new error from a raw OS error number (errno). + pub fn from_os_error(errno: i32) -> Self { + assert!(errno > 0); + Self(NonZeroU32::new(errno as u32).unwrap()) + } - /// No generator is available. - pub const UNAVAILABLE: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNAVAILABLE) }); + /// Crate a custom error in the provided block (group of 2^16 error codes). + /// The provided block must not be negative, and block 0 is reserved for + /// custom errors in the `getrandom` crate. + pub fn custom_error(block: i16, code: u16) -> Self { + assert!(block >= 0); + let n = CUSTOM_START + (block as u32) * BLOCK_SIZE + (code as u32); + Self(NonZeroU32::new(n).unwrap()) + } - /// Extract the error code. + /// Extract the raw OS error code (if this error came from the OS) /// - /// This may equal one of the codes defined in this library or may be a - /// system error code. + /// 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 still be formatted via the `Diplay` implementation. + pub fn raw_os_error(&self) -> Option { + self.try_os_error().ok() + } + + /// Extract the bare error code. /// - /// One may attempt to format this error via the `Display` implementation. + /// This code can either come from the underlying OS, or be a custom error. + /// Use [`raw_os_error()`] to disambiguate. pub fn code(&self) -> NonZeroU32 { self.0 } - pub(crate) fn msg(&self) -> Option<&'static str> { - if let Some(msg) = crate::imp::error_msg_inner(self.0) { - Some(msg) + /// Helper method for creating internal errors + #[allow(dead_code)] + pub(crate) fn internal(code: u16) -> Self { + Self::custom_error(0, code) + } + + /// Returns either the OS error or a (block, code) pair + fn try_os_error(&self) -> Result { + if self.0.get() < CUSTOM_START { + Ok(self.0.get() as i32) } else { - match *self { - Error::UNKNOWN => Some("getrandom: unknown error"), - Error::UNAVAILABLE => Some("getrandom: unavailable"), - _ => None, - } + let offset = self.0.get() - CUSTOM_START; + Err(((offset / BLOCK_SIZE) as i16, (offset % BLOCK_SIZE) as u16)) } } } +#[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 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), + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut dbg = f.debug_struct("Error"); + match self.try_os_error() { + Ok(errno) => { + dbg.field("os_error", &errno); + let mut buf = [0u8; 128]; + if let Some(desc) = os_err_desc(errno, &mut buf) { + dbg.field("description", &desc); + } + } + Err((0, code)) => { + dbg.field("internal_code", &code); + if let Some(desc) = internal_desc(code) { + dbg.field("description", &desc); + } + } + Err((block, code)) => { + dbg.field("block", &block); + dbg.field("custom_code", &code); + } } + dbg.finish() } } 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), + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.try_os_error() { + Ok(errno) => { + let mut buf = [0u8; 128]; + match os_err_desc(errno, &mut buf) { + Some(desc) => f.write_str(desc), + None => write!(f, "OS Error: {}", errno), + } + } + Err((0, code)) => match internal_desc(code) { + Some(desc) => f.write_str(desc), + None => write!(f, "Internal Error: {}", code), + }, + Err((block, code)) => write!(f, "Custom Error: block={}, code={}", block, code), } } } impl From for Error { fn from(code: NonZeroU32) -> Self { - Error(code) + Self(code) } } -impl From<&Error> for Error { - fn from(error: &Error) -> Self { - *error +/// Internal Error constants +pub(crate) const UNSUPPORTED: u16 = 0; +pub(crate) const UNKNOWN_IO_ERROR: u16 = 1; +pub(crate) const SEC_RANDOM_FAILED: u16 = 2; +pub(crate) const RTL_GEN_RANDOM_FAILED: u16 = 3; +pub(crate) const FAILED_RDRAND: u16 = 4; +pub(crate) const NO_RDRAND: u16 = 5; +pub(crate) const BINDGEN_CRYPTO_UNDEF: u16 = 6; +pub(crate) const BINDGEN_GRV_UNDEF: u16 = 7; +pub(crate) const STDWEB_NO_RNG: u16 = 8; +pub(crate) const STDWEB_RNG_FAILED: u16 = 9; + +fn internal_desc(code: u16) -> Option<&'static str> { + match code { + UNSUPPORTED => Some("getrandom: this target is not supported"), + UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"), + SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"), + RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"), + FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), + NO_RDRAND => Some("RDRAND: instruction not supported"), + BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"), + BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"), + STDWEB_NO_RNG => Some("stdweb: no randomness source available"), + STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"), + _ => None, } } diff --git a/src/error_impls.rs b/src/error_impls.rs index 6bd2c69a..db7aea82 100644 --- a/src/error_impls.rs +++ b/src/error_impls.rs @@ -7,28 +7,26 @@ // except according to those terms. extern crate std; -use crate::error::Error; +use crate::{error::UNKNOWN_IO_ERROR, Error}; use core::convert::From; -use core::num::NonZeroU32; -use std::{error, io}; +use std::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) => Self::from_os_error(errno), + None => Self::internal(UNKNOWN_IO_ERROR), + } } } 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), } } } -impl error::Error for Error {} +impl std::error::Error for Error {} diff --git a/src/freebsd.rs b/src/freebsd.rs index aa77b221..61feaf73 100644 --- a/src/freebsd.rs +++ b/src/freebsd.rs @@ -9,7 +9,6 @@ //! Implementation for FreeBSD use crate::util_libc::{sys_fill_exact, Weak}; use crate::Error; -use core::num::NonZeroU32; use core::{mem, ptr}; type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; @@ -44,8 +43,3 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { sys_fill_exact(dest, kern_arnd) } } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/fuchsia.rs b/src/fuchsia.rs index a9f53888..572ff534 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" { @@ -19,8 +18,3 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) } Ok(()) } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/ios.rs b/src/ios.rs index 97d68979..5e7040c8 100644 --- a/src/ios.rs +++ b/src/ios.rs @@ -7,10 +7,7 @@ // except according to those terms. //! Implementation for iOS -extern crate std; - -use crate::Error; -use core::num::NonZeroU32; +use crate::{error::SEC_RANDOM_FAILED, Error}; // TODO: Make extern once extern_types feature is stabilized. See: // https://github.com/rust-lang/rust/issues/43467 @@ -27,14 +24,8 @@ 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::internal(SEC_RANDOM_FAILED)) } else { Ok(()) } } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/lib.rs b/src/lib.rs index d98d8584..156b9014 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,25 +79,8 @@ //! `getrandom`, hence after the first successful call one can be reasonably //! confident that no errors will occur. //! -//! On unsupported platforms, `getrandom` always fails with [`Error::UNAVAILABLE`]. -//! -//! ## Error codes -//! The crate uses the following custom error codes: -//! - `0x57f4c500` (dec: 1475659008) - an unknown error. Constant: -//! [`Error::UNKNOWN`] -//! - `0x57f4c501` (dec: 1475659009) - no generator is available. Constant: -//! [`Error::UNAVAILABLE`] -//! - `0x57f4c580` (dec: 1475659136) - `self.crypto` is undefined, -//! `wasm-bindgen` specific error. -//! - `0x57f4c581` (dec: 1475659137) - `crypto.getRandomValues` is undefined, -//! `wasm-bindgen` specific error. -//! -//! These codes are provided for reference only and should not be matched upon -//! (but you can match on `Error` constants). The codes may change in future and -//! such change will not be considered a breaking one. -//! -//! Other error codes will originate from an underlying system. In case if such -//! error is encountered, please consult with your system documentation. +//! On unsupported platforms, `getrandom` always fails. See the [`Error`] type +//! for more information on what data is returned on failure. //! //! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html //! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html @@ -197,7 +180,6 @@ mod error_impls; target_os = "solaris", target_os = "illumos", ))] -#[allow(dead_code)] mod use_file; // System-specific implementations. diff --git a/src/linux_android.rs b/src/linux_android.rs index 8aa1d58d..0f10e08f 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -7,13 +7,9 @@ // except according to those terms. //! Implementation for Linux / Android -extern crate std; - use crate::util::LazyBool; -use crate::util_libc::sys_fill_exact; +use crate::util_libc::{last_os_error, sys_fill_exact}; use crate::{use_file, Error}; -use core::num::NonZeroU32; -use std::io; pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { static HAS_GETRANDOM: LazyBool = LazyBool::new(); @@ -29,7 +25,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { fn is_getrandom_available() -> bool { let res = unsafe { libc::syscall(libc::SYS_getrandom, 0, 0, libc::GRND_NONBLOCK) }; if res < 0 { - match io::Error::last_os_error().raw_os_error() { + match last_os_error().raw_os_error() { Some(libc::ENOSYS) => false, // No kernel support Some(libc::EPERM) => false, // Blocked by seccomp _ => true, @@ -38,8 +34,3 @@ fn is_getrandom_available() -> bool { true } } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/macos.rs b/src/macos.rs index 84d95565..c3bc5334 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(()) @@ -35,8 +32,3 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { use_file::getrandom_inner(dest) } } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/openbsd.rs b/src/openbsd.rs index e213e5f5..e1ac179f 100644 --- a/src/openbsd.rs +++ b/src/openbsd.rs @@ -7,24 +7,17 @@ // except according to those terms. //! Implementation for OpenBSD -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> { - None -} diff --git a/src/rdrand.rs b/src/rdrand.rs index c55d2074..d0c1653d 100644 --- a/src/rdrand.rs +++ b/src/rdrand.rs @@ -7,12 +7,12 @@ // except according to those terms. //! Implementation for SGX using RDRAND instruction +use crate::error::{FAILED_RDRAND, NO_RDRAND}; #[cfg(not(target_feature = "rdrand"))] use crate::util::LazyBool; use crate::Error; use core::arch::x86_64::_rdrand64_step; use core::mem; -use core::num::NonZeroU32; // Recommendation from "IntelĀ® Digital Random Number Generator (DRNG) Software // Implementation Guide" - Section 5.2.1 and "IntelĀ® 64 and IA-32 Architectures @@ -37,8 +37,7 @@ unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { // Keep looping in case this was a false positive. } } - error!("RDRAND failed, CPU issue likely"); - Err(Error::UNKNOWN) + Err(Error::internal(FAILED_RDRAND)) } // "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653. @@ -65,7 +64,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::internal(NO_RDRAND)); } // SAFETY: After this point, rdrand is supported, so calling the rdrand @@ -89,8 +88,3 @@ unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> { } Ok(()) } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs index 8539971b..7550fce9 100644 --- a/src/solaris_illumos.rs +++ b/src/solaris_illumos.rs @@ -20,7 +20,6 @@ use crate::util_libc::{sys_fill_exact, Weak}; use crate::{use_file, Error}; use core::mem; -use core::num::NonZeroU32; #[cfg(target_os = "illumos")] type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; @@ -43,8 +42,3 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { use_file::getrandom_inner(dest) } } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/use_file.rs b/src/use_file.rs index adc1b3cd..74a12ef7 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; @@ -60,9 +56,3 @@ fn init_file() -> Option { } Some(File::open(FILE_PATH).ok()?.into_raw_fd()) } - -#[inline(always)] -#[allow(dead_code)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/util_libc.rs b/src/util_libc.rs index 606bc1ff..033a2f86 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -5,12 +5,27 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate std; - use crate::util::LazyUsize; use crate::Error; use core::ptr::NonNull; -use std::io; + +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", target_os = "redox"))] { + 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", target_os = "dragonfly"))] { + use libc::__error as errno_location; + } else if #[cfg(target_os = "haiku")] { + use libc::_errnop as errno_location; + } +} + +pub fn last_os_error() -> Error { + Error::from_os_error(unsafe { *errno_location() }) +} // Fill a buffer by repeatedly invoking a system call. The `sys_fill` function: // - should return -1 and set errno on failure @@ -22,10 +37,10 @@ pub fn sys_fill_exact( while !buf.is_empty() { let res = sys_fill(buf); if res < 0 { - let err = io::Error::last_os_error(); + 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.into()); + return Err(err); } } else { // We don't check for EOF (ret = 0) as the data we are reading diff --git a/src/wasi.rs b/src/wasi.rs index 10b91788..33c272ad 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -8,20 +8,14 @@ //! 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.into())) } else { Ok(()) // Zero means success for WASI } } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/wasm32_bindgen.rs b/src/wasm32_bindgen.rs index 08df426b..16fea6af 100644 --- a/src/wasm32_bindgen.rs +++ b/src/wasm32_bindgen.rs @@ -11,17 +11,13 @@ 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::{BINDGEN_CRYPTO_UNDEF, BINDGEN_GRV_UNDEF}; use crate::Error; -const CODE_CRYPTO_UNDEF: u32 = CODE_PREFIX | 0x80; -const CODE_GRV_UNDEF: u32 = CODE_PREFIX | 0x81; - #[derive(Clone, Debug)] enum RngSource { Node(NodeCrypto), @@ -83,17 +79,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::internal(BINDGEN_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::internal(BINDGEN_GRV_UNDEF)); } // Ok! `self.crypto.getRandomValues` is a defined value, so let's @@ -101,15 +93,6 @@ fn getrandom_init() -> Result { Ok(RngSource::Browser(crypto)) } -#[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"), - _ => None, - } -} - #[wasm_bindgen] extern "C" { type Function; diff --git a/src/wasm32_stdweb.rs b/src/wasm32_stdweb.rs index 32a5686c..10593a1d 100644 --- a/src/wasm32_stdweb.rs +++ b/src/wasm32_stdweb.rs @@ -8,12 +8,12 @@ //! Implementation for WASM via stdweb use core::mem; -use core::num::NonZeroU32; use stdweb::unstable::TryInto; use stdweb::web::error::Error as WebError; use stdweb::{_js_impl, js}; +use crate::error::{STDWEB_NO_RNG, STDWEB_RNG_FAILED}; use crate::Error; use std::sync::Once; @@ -26,7 +26,7 @@ enum RngSource { pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { assert_eq!(mem::size_of::(), 4); static ONCE: Once = Once::new(); - static mut RNG_SOURCE: Result = Err(Error::UNAVAILABLE); + static mut RNG_SOURCE: Result = Ok(RngSource::Node); // SAFETY: RNG_SOURCE is only written once, before being read. ONCE.call_once(|| unsafe { @@ -69,7 +69,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::internal(STDWEB_NO_RNG)) } } @@ -105,13 +105,8 @@ 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::internal(STDWEB_RNG_FAILED)); } } Ok(()) } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} diff --git a/src/windows.rs b/src/windows.rs index c52e9f10..6590cedc 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -7,10 +7,7 @@ // except according to those terms. //! Implementation for Windows -extern crate std; - -use crate::Error; -use core::num::NonZeroU32; +use crate::{error::RTL_GEN_RANDOM_FAILED, Error}; extern "system" { #[link_name = "SystemFunction036"] @@ -22,14 +19,8 @@ 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::internal(RTL_GEN_RANDOM_FAILED)); } } Ok(()) } - -#[inline(always)] -pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { - None -} From 7bb743b18835cfd25c0fd6467e8f1847a0c40d4e Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 8 Jul 2019 23:53:13 -0700 Subject: [PATCH 2/7] Cleanup lib.rs --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 156b9014..03e1cc48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,14 +129,12 @@ cfg_if! { } } -#[cfg(feature = "std")] -extern crate std; - mod error; pub use crate::error::Error; #[allow(dead_code)] mod util; +// Unlike the other Unix, Fuchsia and iOS don't use the libc to make any calls. #[cfg(any( target_os = "android", target_os = "dragonfly", From c71ad9a3a74ebcdeb5aed1742350ff75d55a4581 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 9 Jul 2019 13:31:11 -0700 Subject: [PATCH 3/7] Fixed formatting, inline, and conversions --- src/error.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index ad7ec9d2..18ea3158 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,6 +24,7 @@ const BLOCK_SIZE: u32 = 1 << 16; impl Error { /// Create a new error from a raw OS error number (errno). + #[inline] pub fn from_os_error(errno: i32) -> Self { assert!(errno > 0); Self(NonZeroU32::new(errno as u32).unwrap()) @@ -32,9 +33,10 @@ impl Error { /// Crate a custom error in the provided block (group of 2^16 error codes). /// The provided block must not be negative, and block 0 is reserved for /// custom errors in the `getrandom` crate. + #[inline] pub fn custom_error(block: i16, code: u16) -> Self { assert!(block >= 0); - let n = CUSTOM_START + (block as u32) * BLOCK_SIZE + (code as u32); + let n = CUSTOM_START + (block as u16 as u32) * BLOCK_SIZE + (code as u32); Self(NonZeroU32::new(n).unwrap()) } @@ -43,6 +45,7 @@ impl Error { /// 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 still be formatted via the `Diplay` implementation. + #[inline] pub fn raw_os_error(&self) -> Option { self.try_os_error().ok() } @@ -51,6 +54,7 @@ impl Error { /// /// This code can either come from the underlying OS, or be a custom error. /// Use [`raw_os_error()`] to disambiguate. + #[inline] pub fn code(&self) -> NonZeroU32 { self.0 } From c9c82df5099f46e8271cdd0d5d825c307f23ce10 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 26 Jul 2019 04:23:11 -0700 Subject: [PATCH 4/7] Simplify Error type and remove methods --- src/cloudabi.rs | 5 +- src/dummy.rs | 2 +- src/error.rs | 133 +++++++++++++++++------------------------- src/error_impls.rs | 9 ++- src/ios.rs | 2 +- src/rdrand.rs | 4 +- src/util_libc.rs | 3 +- src/wasi.rs | 7 ++- src/wasm32_bindgen.rs | 4 +- src/wasm32_stdweb.rs | 4 +- src/windows.rs | 2 +- 11 files changed, 76 insertions(+), 99 deletions(-) diff --git a/src/cloudabi.rs b/src/cloudabi.rs index e647a9f4..d3d09289 100644 --- a/src/cloudabi.rs +++ b/src/cloudabi.rs @@ -8,6 +8,7 @@ //! Implementation for CloudABI use crate::Error; +use core::num::NonZeroU32; extern "C" { fn cloudabi_sys_random_get(buf: *mut u8, buf_len: usize) -> u16; @@ -15,9 +16,9 @@ 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 errno != 0 { + if let Some(code) = NonZeroU32::new(errno as u32) { error!("cloudabi_sys_random_get: failed with {}", errno); - Err(Error::from_os_error(errno.into())) + Err(Error::from(code)) } else { Ok(()) // Zero means success for CloudABI } diff --git a/src/dummy.rs b/src/dummy.rs index 1f5f6ddc..0c24ba0f 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -10,5 +10,5 @@ use crate::{error::UNSUPPORTED, Error}; pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> { - Err(Error::internal(UNSUPPORTED)) + Err(UNSUPPORTED) } diff --git a/src/error.rs b/src/error.rs index 18ea3158..fb0f3368 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,32 +13,21 @@ use core::num::NonZeroU32; /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and /// if so, which error code the OS gave the application. If such an error is /// encountered, please consult with your system documentation. +/// +/// Internally this type is a NonZeroU32, with certain values reserved for +/// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Error(NonZeroU32); -// This NonZeroU32 in Error has enough room for two types of errors: -// - OS Errors: in range [1, 1 << 31) (i.e. positive i32 values) -// - Custom Errors: in range [1 << 31, 1 << 32) (in blocks of 1 << 16) -const CUSTOM_START: u32 = 1 << 31; -const BLOCK_SIZE: u32 = 1 << 16; - impl Error { - /// Create a new error from a raw OS error number (errno). - #[inline] - pub fn from_os_error(errno: i32) -> Self { - assert!(errno > 0); - Self(NonZeroU32::new(errno as u32).unwrap()) - } + /// Codes below this point represent OS Errors (i.e. positive i32 values). + /// Codes at or above this point, but below [`Error::CUSTOM_START`] are + /// reserved for use by the `rand` and `getrandom` crates. + pub const INTERNAL_START: u32 = 1 << 31; - /// Crate a custom error in the provided block (group of 2^16 error codes). - /// The provided block must not be negative, and block 0 is reserved for - /// custom errors in the `getrandom` crate. - #[inline] - pub fn custom_error(block: i16, code: u16) -> Self { - assert!(block >= 0); - let n = CUSTOM_START + (block as u16 as u32) * BLOCK_SIZE + (code as u32); - Self(NonZeroU32::new(n).unwrap()) - } + /// Codes at or above this point can be used by users to define their own + /// custom errors. + pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); /// Extract the raw OS error code (if this error came from the OS) /// @@ -47,33 +36,21 @@ impl Error { /// error value can still be formatted via the `Diplay` implementation. #[inline] pub fn raw_os_error(&self) -> Option { - self.try_os_error().ok() + if self.0.get() < Self::INTERNAL_START { + Some(self.0.get() as i32) + } else { + None + } } /// Extract the bare error code. /// /// This code can either come from the underlying OS, or be a custom error. - /// Use [`raw_os_error()`] to disambiguate. + /// Use [`Error::raw_os_error()`] to disambiguate. #[inline] pub fn code(&self) -> NonZeroU32 { self.0 } - - /// Helper method for creating internal errors - #[allow(dead_code)] - pub(crate) fn internal(code: u16) -> Self { - Self::custom_error(0, code) - } - - /// Returns either the OS error or a (block, code) pair - fn try_os_error(&self) -> Result { - if self.0.get() < CUSTOM_START { - Ok(self.0.get() as i32) - } else { - let offset = self.0.get() - CUSTOM_START; - Err(((offset / BLOCK_SIZE) as i16, (offset % BLOCK_SIZE) as u16)) - } - } } #[cfg(any(unix, target_os = "redox"))] @@ -96,24 +73,17 @@ fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> { impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut dbg = f.debug_struct("Error"); - match self.try_os_error() { - Ok(errno) => { - dbg.field("os_error", &errno); - let mut buf = [0u8; 128]; - if let Some(desc) = os_err_desc(errno, &mut buf) { - dbg.field("description", &desc); - } - } - Err((0, code)) => { - dbg.field("internal_code", &code); - if let Some(desc) = internal_desc(code) { - dbg.field("description", &desc); - } - } - Err((block, code)) => { - dbg.field("block", &block); - dbg.field("custom_code", &code); + 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(desc) = internal_desc(*self) { + dbg.field("internal_code", &self.0.get()); + dbg.field("description", &desc); + } else { + dbg.field("unknown_code", &self.0.get()); } dbg.finish() } @@ -121,19 +91,16 @@ impl fmt::Debug for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.try_os_error() { - Ok(errno) => { - let mut buf = [0u8; 128]; - match os_err_desc(errno, &mut buf) { - Some(desc) => f.write_str(desc), - None => write!(f, "OS Error: {}", errno), - } - } - Err((0, code)) => match internal_desc(code) { + if let Some(errno) = self.raw_os_error() { + let mut buf = [0u8; 128]; + match os_err_desc(errno, &mut buf) { Some(desc) => f.write_str(desc), - None => write!(f, "Internal Error: {}", code), - }, - Err((block, code)) => write!(f, "Custom Error: block={}, code={}", block, code), + None => write!(f, "OS Error: {}", errno), + } + } else if let Some(desc) = internal_desc(*self) { + f.write_str(desc) + } else { + write!(f, "Unknown Error: {}", self.0.get()) } } } @@ -145,19 +112,23 @@ impl From for Error { } /// Internal Error constants -pub(crate) const UNSUPPORTED: u16 = 0; -pub(crate) const UNKNOWN_IO_ERROR: u16 = 1; -pub(crate) const SEC_RANDOM_FAILED: u16 = 2; -pub(crate) const RTL_GEN_RANDOM_FAILED: u16 = 3; -pub(crate) const FAILED_RDRAND: u16 = 4; -pub(crate) const NO_RDRAND: u16 = 5; -pub(crate) const BINDGEN_CRYPTO_UNDEF: u16 = 6; -pub(crate) const BINDGEN_GRV_UNDEF: u16 = 7; -pub(crate) const STDWEB_NO_RNG: u16 = 8; -pub(crate) const STDWEB_RNG_FAILED: u16 = 9; - -fn internal_desc(code: u16) -> Option<&'static str> { - match code { +pub(crate) const UNSUPPORTED: Error = internal_error(0); +pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error(1); +pub(crate) const SEC_RANDOM_FAILED: Error = internal_error(2); +pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error(3); +pub(crate) const FAILED_RDRAND: Error = internal_error(4); +pub(crate) const NO_RDRAND: Error = internal_error(5); +pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error(6); +pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error(7); +pub(crate) const STDWEB_NO_RNG: Error = internal_error(8); +pub(crate) const STDWEB_RNG_FAILED: Error = internal_error(9); + +const fn internal_error(n: u16) -> Error { + Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + n as u32) }) +} + +fn internal_desc(error: Error) -> Option<&'static str> { + match error { UNSUPPORTED => Some("getrandom: this target is not supported"), UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"), SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"), diff --git a/src/error_impls.rs b/src/error_impls.rs index db7aea82..007472e4 100644 --- a/src/error_impls.rs +++ b/src/error_impls.rs @@ -9,14 +9,17 @@ extern crate std; use crate::{error::UNKNOWN_IO_ERROR, Error}; use core::convert::From; +use core::num::NonZeroU32; use std::io; impl From for Error { fn from(err: io::Error) -> Self { - match err.raw_os_error() { - Some(errno) => Self::from_os_error(errno), - None => Self::internal(UNKNOWN_IO_ERROR), + if let Some(errno) = err.raw_os_error() { + if let Some(code) = NonZeroU32::new(errno as u32) { + return Error::from(code); + } } + UNKNOWN_IO_ERROR } } diff --git a/src/ios.rs b/src/ios.rs index 5e7040c8..30c008c2 100644 --- a/src/ios.rs +++ b/src/ios.rs @@ -24,7 +24,7 @@ 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 { - Err(Error::internal(SEC_RANDOM_FAILED)) + Err(SEC_RANDOM_FAILED) } else { Ok(()) } diff --git a/src/rdrand.rs b/src/rdrand.rs index d0c1653d..96229673 100644 --- a/src/rdrand.rs +++ b/src/rdrand.rs @@ -37,7 +37,7 @@ unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { // Keep looping in case this was a false positive. } } - Err(Error::internal(FAILED_RDRAND)) + Err(FAILED_RDRAND) } // "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653. @@ -64,7 +64,7 @@ fn is_rdrand_supported() -> bool { pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { if !is_rdrand_supported() { - return Err(Error::internal(NO_RDRAND)); + return Err(NO_RDRAND); } // SAFETY: After this point, rdrand is supported, so calling the rdrand diff --git a/src/util_libc.rs b/src/util_libc.rs index 033a2f86..89c610c3 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -7,6 +7,7 @@ // except according to those terms. use crate::util::LazyUsize; use crate::Error; +use core::num::NonZeroU32; use core::ptr::NonNull; cfg_if! { @@ -24,7 +25,7 @@ cfg_if! { } pub fn last_os_error() -> Error { - Error::from_os_error(unsafe { *errno_location() }) + Error::from(NonZeroU32::new(unsafe { *errno_location() } as u32).unwrap()) } // Fill a buffer by repeatedly invoking a system call. The `sys_fill` function: diff --git a/src/wasi.rs b/src/wasi.rs index 33c272ad..b458bdf8 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -8,13 +8,14 @@ //! 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 ret != 0 { - error!("WASI: __wasi_random_get: failed with {}", ret); - Err(Error::from_os_error(ret.into())) + if let Some(code) = NonZeroU32::new(ret as u32) { + error!("WASI: __wasi_random_get: failed with {}", errno); + Err(Error::from(code)) } else { Ok(()) // Zero means success for WASI } diff --git a/src/wasm32_bindgen.rs b/src/wasm32_bindgen.rs index 16fea6af..c6eef5fd 100644 --- a/src/wasm32_bindgen.rs +++ b/src/wasm32_bindgen.rs @@ -79,13 +79,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::internal(BINDGEN_CRYPTO_UNDEF)); + return Err(BINDGEN_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::internal(BINDGEN_GRV_UNDEF)); + return Err(BINDGEN_GRV_UNDEF); } // Ok! `self.crypto.getRandomValues` is a defined value, so let's diff --git a/src/wasm32_stdweb.rs b/src/wasm32_stdweb.rs index 10593a1d..c59c9c42 100644 --- a/src/wasm32_stdweb.rs +++ b/src/wasm32_stdweb.rs @@ -69,7 +69,7 @@ fn getrandom_init() -> Result { } else { let err: WebError = js! { return @{ result }.error }.try_into().unwrap(); error!("getrandom unavailable: {}", err); - Err(Error::internal(STDWEB_NO_RNG)) + Err(STDWEB_NO_RNG) } } @@ -105,7 +105,7 @@ 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::internal(STDWEB_RNG_FAILED)); + return Err(STDWEB_RNG_FAILED); } } Ok(()) diff --git a/src/windows.rs b/src/windows.rs index 6590cedc..e1b8df66 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -19,7 +19,7 @@ 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 { - return Err(Error::internal(RTL_GEN_RANDOM_FAILED)); + return Err(RTL_GEN_RANDOM_FAILED); } } Ok(()) From c6452d3a70a31d6eda0181f4e6f205f996a9fa88 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 26 Jul 2019 04:42:54 -0700 Subject: [PATCH 5/7] Fix copy/paste error --- src/wasi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasi.rs b/src/wasi.rs index b458bdf8..0920752f 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -14,7 +14,7 @@ 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 {}", errno); + error!("WASI: __wasi_random_get: failed with {}", ret); Err(Error::from(code)) } else { Ok(()) // Zero means success for WASI From 387f2e756b9cdcc3763bfa2ce4d44b0fc15c7e1f Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 26 Jul 2019 17:46:51 -0700 Subject: [PATCH 6/7] Address review feedback --- src/error.rs | 39 +++++++++++++++++++++++++-------------- src/util_libc.rs | 8 +++++++- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/error.rs b/src/error.rs index fb0f3368..49ce5cb4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,6 +20,11 @@ use core::num::NonZeroU32; pub struct Error(NonZeroU32); impl Error { + #[deprecated(since = "0.1.7")] + pub const UNKNOWN: Error = UNSUPPORTED; + #[deprecated(since = "0.1.7")] + pub const UNAVAILABLE: Error = UNSUPPORTED; + /// Codes below this point represent OS Errors (i.e. positive i32 values). /// Codes at or above this point, but below [`Error::CUSTOM_START`] are /// reserved for use by the `rand` and `getrandom` crates. @@ -111,25 +116,31 @@ impl From for Error { } } -/// Internal Error constants -pub(crate) const UNSUPPORTED: Error = internal_error(0); -pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error(1); -pub(crate) const SEC_RANDOM_FAILED: Error = internal_error(2); -pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error(3); -pub(crate) const FAILED_RDRAND: Error = internal_error(4); -pub(crate) const NO_RDRAND: Error = internal_error(5); -pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error(6); -pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error(7); -pub(crate) const STDWEB_NO_RNG: Error = internal_error(8); -pub(crate) const STDWEB_RNG_FAILED: Error = internal_error(9); - -const fn internal_error(n: u16) -> Error { - Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + n as u32) }) +// TODO: Convert to a function when min_version >= 1.33 +macro_rules! internal_error { + ($code:expr) => {{ + let n: u16 = $code; + Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + n as u32) }) + }}; } +/// Internal Error constants +pub(crate) const UNSUPPORTED: Error = internal_error!(0); +pub(crate) const ERRNO_NOT_POSITIVE: Error = internal_error!(1); +pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error!(2); +pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3); +pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4); +pub(crate) const FAILED_RDRAND: Error = internal_error!(5); +pub(crate) const NO_RDRAND: Error = internal_error!(6); +pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7); +pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8); +pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9); +pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10); + fn internal_desc(error: Error) -> Option<&'static str> { match error { UNSUPPORTED => Some("getrandom: this target is not supported"), + ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"), SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"), RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"), diff --git a/src/util_libc.rs b/src/util_libc.rs index 89c610c3..015d1a04 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -5,6 +5,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +use crate::error::ERRNO_NOT_POSITIVE; use crate::util::LazyUsize; use crate::Error; use core::num::NonZeroU32; @@ -25,7 +26,12 @@ cfg_if! { } pub fn last_os_error() -> Error { - Error::from(NonZeroU32::new(unsafe { *errno_location() } as u32).unwrap()) + let errno = unsafe { *errno_location() }; + if errno > 0 { + Error::from(NonZeroU32::new(errno as u32).unwrap()) + } else { + ERRNO_NOT_POSITIVE + } } // Fill a buffer by repeatedly invoking a system call. The `sys_fill` function: From 8fa54bfa36d37634c117eab559a7b9ae61ce162b Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 26 Jul 2019 17:57:47 -0700 Subject: [PATCH 7/7] No `let` in constants --- src/error.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 49ce5cb4..a61f2e4d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -118,10 +118,9 @@ impl From for Error { // TODO: Convert to a function when min_version >= 1.33 macro_rules! internal_error { - ($code:expr) => {{ - let n: u16 = $code; - Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + n as u32) }) - }}; + ($n:expr) => { + Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + $n as u16 as u32) }) + }; } /// Internal Error constants