Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/cloudabi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@

//! Implementation for CloudABI
use crate::Error;
use core::num::NonZeroU32;

extern "C" {
fn cloudabi_sys_random_get(buf: *mut u8, buf_len: usize) -> u16;
}

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
}
Expand Down
2 changes: 1 addition & 1 deletion src/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
use crate::{error::UNSUPPORTED, Error};

pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> {
Err(Error::internal(UNSUPPORTED))
Err(UNSUPPORTED)
}
133 changes: 52 additions & 81 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
///
Expand All @@ -47,33 +36,21 @@ impl Error {
/// error value can still be formatted via the `Diplay` implementation.
#[inline]
pub fn raw_os_error(&self) -> Option<i32> {
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 {
Comment thread
josephlr marked this conversation as resolved.
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<i32, (i16, u16)> {
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"))]
Expand All @@ -96,44 +73,34 @@ 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()
}
}

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())
}
}
}
Expand All @@ -145,19 +112,23 @@ impl From<NonZeroU32> 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);
Comment thread
newpavlov marked this conversation as resolved.
Outdated
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"),
Expand Down
9 changes: 6 additions & 3 deletions src/error_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<io::Error> 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
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
4 changes: 2 additions & 2 deletions src/rdrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/util_libc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand All @@ -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())
Comment thread
newpavlov marked this conversation as resolved.
Outdated
}

// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function:
Expand Down
7 changes: 4 additions & 3 deletions src/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment thread
josephlr marked this conversation as resolved.
Outdated
Err(Error::from(code))
} else {
Ok(()) // Zero means success for WASI
}
Expand Down
4 changes: 2 additions & 2 deletions src/wasm32_bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ fn getrandom_init() -> Result<RngSource, Error> {
// 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
Expand Down
4 changes: 2 additions & 2 deletions src/wasm32_stdweb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn getrandom_init() -> Result<RngSource, Error> {
} 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)
}
}

Expand Down Expand Up @@ -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(())
Expand Down
2 changes: 1 addition & 1 deletion src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down