Skip to content

Revise error types and codes #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ matrix:
- rm -rf target/doc
- cargo doc --no-deps --all --all-features
- cargo deadlinks --dir target/doc
# also test minimum dependency versions are usable
- cargo generate-lockfile -Z minimal-versions
- cargo test

- rust: nightly
os: osx
Expand All @@ -52,6 +55,9 @@ matrix:
- rm -rf target/doc
- cargo doc --no-deps --all --all-features
- cargo deadlinks --dir target/doc
# also test minimum dependency versions are usable
- cargo generate-lockfile -Z minimal-versions
- cargo test

- rust: nightly
env: DESCRIPTION="WASM via emscripten, stdweb and wasm-bindgen"
Expand Down Expand Up @@ -97,6 +103,14 @@ matrix:
#- cargo build --target=x86_64-unknown-fuchsia --all-features
- cargo build --target=x86_64-unknown-netbsd --all-features
- cargo build --target=x86_64-unknown-redox --all-features
# also test minimum dependency versions are usable
- cargo generate-lockfile -Z minimal-versions
- cargo build --target=x86_64-sun-solaris --all-features
- cargo build --target=x86_64-unknown-cloudabi --all-features
- cargo build --target=x86_64-unknown-freebsd --all-features
#- cargo build --target=x86_64-unknown-fuchsia --all-features
- cargo build --target=x86_64-unknown-netbsd --all-features
- cargo build --target=x86_64-unknown-redox --all-features

# Trust cross-built/emulated targets. We must repeat all non-default values.
- rust: stable
Expand Down
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ members = [
"tests/wasm_bindgen",
]

[dependencies]
log = { version = "0.4", optional = true }

[target.'cfg(unix)'.dependencies]
libc = "0.2"
# In general, we need at least 0.2.27. On Solaris, we need some unknown newer version.
libc = "0.2.50"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "winnt"] }
winapi = { version = "0.3.6", features = ["minwindef", "ntsecapi", "winnt"] }

[target.'cfg(target_os = "cloudabi")'.dependencies]
cloudabi = "0.0.3"
Expand All @@ -27,5 +31,5 @@ cloudabi = "0.0.3"
fuchsia-cprng = "0.1"

[target.wasm32-unknown-unknown.dependencies]
wasm-bindgen = { version = "0.2.12", optional = true }
stdweb = { version = "0.4", optional = true }
wasm-bindgen = { version = "0.2.29", optional = true }
stdweb = { version = "0.4.9", optional = true }
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ fn get_random_buf() -> Result<[u8; 32], getrandom::Error> {

This library is `no_std` compatible on SGX but requires `std` on most platforms.

The `log` library is supported as an optional dependency. If enabled, error
reporting will be improved on some platforms.

For WebAssembly (`wasm32`), Enscripten targets are supported directly; otherwise
one of the following features must be enabled:

Expand Down
6 changes: 5 additions & 1 deletion src/cloudabi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@
extern crate cloudabi;

use core::num::NonZeroU32;
use error::Error;
use Error;

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
let errno = unsafe { cloudabi::random_get(dest) };
if errno == cloudabi::errno::SUCCESS {
Ok(())
} else {
let code = NonZeroU32::new(errno as u32).unwrap();
error!("cloudabi::random_get syscall failed with code {}", code);
Err(Error::from(code))
}
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
8 changes: 6 additions & 2 deletions src/dragonfly_haiku.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
// except according to those terms.

//! Implementation for DragonFly / Haiku
use super::Error;
use super::utils::use_init;
use Error;
use utils::use_init;
use std::fs::File;
use std::io::Read;
use std::cell::RefCell;
use std::num::NonZeroU32;

thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));

Expand All @@ -23,3 +24,6 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
)
})
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
13 changes: 9 additions & 4 deletions src/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@
// except according to those terms.

//! A dummy implementation for unsupported targets which always returns
//! `Err(UNAVAILABLE_ERROR)`
use super::UNAVAILABLE_ERROR;
//! `Err(ERROR_UNAVAILABLE)`
use std::num::NonZeroU32;
use {Error, ERROR_UNAVAILABLE};

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
Err(UNAVAILABLE_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 }
8 changes: 6 additions & 2 deletions src/emscripten.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
// except according to those terms.

//! Implementation for Emscripten
use super::Error;
use Error;
use std::fs::File;
use std::io::Read;
use std::cell::RefCell;
use super::utils::use_init;
use std::num::NonZeroU32;
use utils::use_init;

thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));

Expand All @@ -29,3 +30,6 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
})
})
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
70 changes: 54 additions & 16 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,29 @@ use core::fmt;
#[cfg(not(target_env = "sgx"))]
use std::{io, error};

// A randomly-chosen 16-bit prefix for our codes
pub(crate) const CODE_PREFIX: u32 = 0x57f40000;
const CODE_UNKNOWN: u32 = CODE_PREFIX | 0;
const CODE_UNAVAILABLE: u32 = CODE_PREFIX | 1;
Copy link
Member

@newpavlov newpavlov Mar 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should make UNKNOWN and UNAVAILABLE associated constants of Error type?


/// An unknown error.
pub const UNKNOWN_ERROR: Error = Error(unsafe {
NonZeroU32::new_unchecked(0x756e6b6e) // "unkn"
///
/// This is the following constant: 57F40000 (hex) / 1475608576 (decimal).
pub const ERROR_UNKNOWN: Error = Error(unsafe {
NonZeroU32::new_unchecked(CODE_UNKNOWN)
});

/// No generator is available.
pub const UNAVAILABLE_ERROR: Error = Error(unsafe {
NonZeroU32::new_unchecked(0x4e416e61) // "NAna"
///
/// This is the following constant: 57F40001 (hex) / 1475608577 (decimal).
pub const ERROR_UNAVAILABLE: Error = Error(unsafe {
NonZeroU32::new_unchecked(CODE_UNAVAILABLE)
});

/// The error type.
///
/// This type is small and no-std compatible.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(NonZeroU32);

impl Error {
Expand All @@ -38,14 +47,34 @@ impl Error {
pub fn code(&self) -> NonZeroU32 {
self.0
}

fn msg(&self) -> Option<&'static str> {
if let Some(msg) = super::error_msg_inner(self.0) {
Some(msg)
} else {
match *self {
ERROR_UNKNOWN => Some("getrandom: unknown error"),
ERROR_UNAVAILABLE => Some("getrandom: unavailable"),
_ => 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),
}
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
UNKNOWN_ERROR => write!(f, "Getrandom Error: unknown"),
UNAVAILABLE_ERROR => write!(f, "Getrandom Error: unavailable"),
code => write!(f, "Getrandom Error: {}", code.0.get()),
match self.msg() {
Some(msg) => write!(f, "{}", msg),
None => write!(f, "getrandom: unknown code 0x{:08X}", self.0),
}
}
}
Expand All @@ -63,22 +92,31 @@ impl From<io::Error> for Error {
.and_then(|code| NonZeroU32::new(code as u32))
.map(|code| Error(code))
// in practice this should never happen
.unwrap_or(UNKNOWN_ERROR)
.unwrap_or(ERROR_UNKNOWN)
}
}

#[cfg(not(target_env = "sgx"))]
impl From<Error> for io::Error {
fn from(err: Error) -> Self {
match err {
UNKNOWN_ERROR => io::Error::new(io::ErrorKind::Other,
"getrandom error: unknown"),
UNAVAILABLE_ERROR => io::Error::new(io::ErrorKind::Other,
"getrandom error: entropy source is unavailable"),
code => io::Error::from_raw_os_error(code.0.get() as i32),
match err.msg() {
Some(msg) => io::Error::new(io::ErrorKind::Other, msg),
None => io::Error::from_raw_os_error(err.0.get() as i32),
}
}
}

#[cfg(not(target_env = "sgx"))]
impl error::Error for Error { }

#[cfg(test)]
mod tests {
use std::mem::size_of;
use super::Error;

#[test]
fn test_size() {
assert_eq!(size_of::<Error>(), 4);
assert_eq!(size_of::<Result<(), Error>>(), 4);
}
}
7 changes: 6 additions & 1 deletion src/freebsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
//! Implementation for FreeBSD
extern crate libc;

use super::Error;
use Error;
use core::ptr;
use std::io;
use std::num::NonZeroU32;

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
let mib = [libc::CTL_KERN, libc::KERN_ARND];
Expand All @@ -25,8 +26,12 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
)
};
if ret == -1 || len != chunk.len() {
error!("freebsd: kern.arandom syscall failed");
return Err(io::Error::last_os_error().into());
}
}
Ok(())
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
6 changes: 5 additions & 1 deletion src/fuchsia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
//! Implementation for Fuchsia Zircon
extern crate fuchsia_cprng;

use super::Error;
use std::num::NonZeroU32;
use Error;

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
fuchsia_cprng::cprng_draw(dest);
Ok(())
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
11 changes: 7 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ extern crate wasm_bindgen;
feature = "stdweb"))]
#[macro_use] extern crate stdweb;

#[cfg(feature = "log")] #[macro_use] extern crate log;
#[allow(unused)]
#[cfg(not(feature = "log"))] macro_rules! error { ($($x:tt)*) => () }

#[cfg(any(
target_os = "android",
target_os = "netbsd",
Expand All @@ -124,8 +128,7 @@ extern crate wasm_bindgen;
))]
mod utils;
mod error;
pub use error::{Error, UNKNOWN_ERROR, UNAVAILABLE_ERROR};

pub use error::{Error, ERROR_UNKNOWN, ERROR_UNAVAILABLE};

// System-specific implementations.
//
Expand All @@ -136,7 +139,7 @@ macro_rules! mod_use {
#[$cond]
mod $module;
#[$cond]
use $module::getrandom_inner;
use $module::{getrandom_inner, error_msg_inner};
}
}

Expand Down Expand Up @@ -221,7 +224,7 @@ mod_use!(
/// In general, `getrandom` will be fast enough for interactive usage, though
/// significantly slower than a user-space CSPRNG; for the latter consider
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom(dest: &mut [u8]) -> Result<(), error::Error> {
getrandom_inner(dest)
}

Expand Down
9 changes: 7 additions & 2 deletions src/linux_android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
//! Implementation for Linux / Android
extern crate libc;

use super::Error;
use super::utils::use_init;
use Error;
use utils::use_init;
use std::fs::File;
use std::io;
use std::io::Read;
use std::cell::RefCell;
use std::num::NonZeroU32;
use std::sync::atomic::{AtomicBool, Ordering};

static RNG_INIT: AtomicBool = AtomicBool::new(false);
Expand All @@ -33,6 +34,7 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> {
libc::syscall(libc::SYS_getrandom, dest.as_mut_ptr(), dest.len(), 0)
};
if ret < 0 || (ret as usize) != dest.len() {
error!("Linux getrandom syscall failed with return value {}", ret);
return Err(io::Error::last_os_error());
}
Ok(())
Expand Down Expand Up @@ -80,3 +82,6 @@ fn is_getrandom_available() -> bool {

AVAILABLE.load(Ordering::Relaxed)
}

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