Skip to content

Commit a5847c2

Browse files
committed
Remove std dependancy for error handling
1 parent b3e609f commit a5847c2

20 files changed

+193
-228
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ log = { version = "0.4", optional = true }
2222
cfg-if = "0.1"
2323

2424
[target.'cfg(any(unix, target_os = "redox", target_os = "wasi"))'.dependencies]
25-
libc = "0.2.54"
25+
libc = "0.2.60"
2626

2727
[target.wasm32-unknown-unknown.dependencies]
2828
wasm-bindgen = { version = "0.2.29", optional = true }

src/cloudabi.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,17 @@
88

99
//! Implementation for CloudABI
1010
use crate::Error;
11-
use core::num::NonZeroU32;
1211

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

1716
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1817
let errno = unsafe { cloudabi_sys_random_get(dest.as_mut_ptr(), dest.len()) };
19-
if let Some(code) = NonZeroU32::new(errno as u32) {
20-
error!("cloudabi_sys_random_get failed with code {}", code);
21-
Err(Error::from(code))
18+
if errno != 0 {
19+
error!("cloudabi_sys_random_get: failed with {}", errno);
20+
Err(Error::from_os_error(errno.into()))
2221
} else {
2322
Ok(()) // Zero means success for CloudABI
2423
}
2524
}
26-
27-
#[inline(always)]
28-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
29-
None
30-
}

src/dummy.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,9 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9-
//! A dummy implementation for unsupported targets which always returns
10-
//! `Err(Error::UNAVAILABLE)`
11-
use crate::Error;
12-
use core::num::NonZeroU32;
9+
//! A dummy implementation for unsupported targets which always fails
10+
use crate::{error::UNSUPPORTED, Error};
1311

1412
pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> {
15-
error!("no support for this platform");
16-
Err(Error::UNAVAILABLE)
17-
}
18-
19-
#[inline(always)]
20-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
21-
None
13+
Err(Error::internal(UNSUPPORTED))
2214
}

src/error.rs

+125-37
Original file line numberDiff line numberDiff line change
@@ -5,78 +5,166 @@
55
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
8-
use core::convert::From;
98
use core::fmt;
109
use core::num::NonZeroU32;
1110

12-
// A randomly-chosen 24-bit prefix for our codes
13-
pub(crate) const CODE_PREFIX: u32 = 0x57f4c500;
14-
const CODE_UNKNOWN: u32 = CODE_PREFIX | 0x00;
15-
const CODE_UNAVAILABLE: u32 = CODE_PREFIX | 0x01;
16-
17-
/// The error type.
11+
/// A small and `no_std` compatible error type.
1812
///
19-
/// This type is small and no-std compatible.
13+
/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
14+
/// if so, which error code the OS gave the application. If such an error is
15+
/// encountered, please consult with your system documentation.
2016
#[derive(Copy, Clone, Eq, PartialEq)]
21-
pub struct Error(pub(crate) NonZeroU32);
17+
pub struct Error(NonZeroU32);
18+
19+
// This NonZeroU32 in Error has enough room for two types of errors:
20+
// - OS Errors: in range [1, 1 << 31) (i.e. positive i32 values)
21+
// - Custom Errors: in range [1 << 31, 1 << 32) (in blocks of 1 << 16)
22+
const CUSTOM_START: u32 = 1 << 31;
23+
const BLOCK_SIZE: u32 = 1 << 16;
2224

2325
impl Error {
24-
/// An unknown error.
25-
pub const UNKNOWN: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNKNOWN) });
26+
/// Create a new error from a raw OS error number (errno).
27+
pub fn from_os_error(errno: i32) -> Self {
28+
assert!(errno > 0);
29+
Self(NonZeroU32::new(errno as u32).unwrap())
30+
}
2631

27-
/// No generator is available.
28-
pub const UNAVAILABLE: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNAVAILABLE) });
32+
/// Crate a custom error in the provided block (group of 2^16 error codes).
33+
/// The provided block must not be negative, and block 0 is reserved for
34+
/// custom errors in the `getrandom` crate.
35+
pub fn custom_error(block: i16, code: u16) -> Self {
36+
assert!(block >= 0);
37+
let n = CUSTOM_START + (block as u32) * BLOCK_SIZE + (code as u32);
38+
Self(NonZeroU32::new(n).unwrap())
39+
}
2940

30-
/// Extract the error code.
41+
/// Extract the raw OS error code (if this error came from the OS)
3142
///
32-
/// This may equal one of the codes defined in this library or may be a
33-
/// system error code.
43+
/// This method is identical to `std::io::Error::raw_os_error()`, except
44+
/// that it works in `no_std` contexts. If this method returns `None`, the
45+
/// error value can still be formatted via the `Diplay` implementation.
46+
pub fn raw_os_error(&self) -> Option<i32> {
47+
self.try_os_error().ok()
48+
}
49+
50+
/// Extract the bare error code.
3451
///
35-
/// One may attempt to format this error via the `Display` implementation.
52+
/// This code can either come from the underlying OS, or be a custom error.
53+
/// Use [`raw_os_error()`] to disambiguate.
3654
pub fn code(&self) -> NonZeroU32 {
3755
self.0
3856
}
3957

40-
pub(crate) fn msg(&self) -> Option<&'static str> {
41-
if let Some(msg) = crate::imp::error_msg_inner(self.0) {
42-
Some(msg)
58+
/// Helper method for creating internal errors
59+
#[allow(dead_code)]
60+
pub(crate) fn internal(code: u16) -> Self {
61+
Self::custom_error(0, code)
62+
}
63+
64+
/// Returns either the OS error or a (block, code) pair
65+
fn try_os_error(&self) -> Result<i32, (i16, u16)> {
66+
if self.0.get() < CUSTOM_START {
67+
Ok(self.0.get() as i32)
4368
} else {
44-
match *self {
45-
Error::UNKNOWN => Some("getrandom: unknown error"),
46-
Error::UNAVAILABLE => Some("getrandom: unavailable"),
47-
_ => None,
48-
}
69+
let offset = self.0.get() - CUSTOM_START;
70+
Err(((offset / BLOCK_SIZE) as i16, (offset % BLOCK_SIZE) as u16))
4971
}
5072
}
5173
}
5274

75+
#[cfg(any(unix, target_os = "redox"))]
76+
fn os_err_desc(errno: i32, buf: &mut [u8]) -> Option<&str> {
77+
let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
78+
if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
79+
return None;
80+
}
81+
82+
// Take up to trailing null byte
83+
let idx = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
84+
core::str::from_utf8(&buf[..idx]).ok()
85+
}
86+
87+
#[cfg(not(any(unix, target_os = "redox")))]
88+
fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
89+
None
90+
}
91+
5392
impl fmt::Debug for Error {
54-
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
55-
match self.msg() {
56-
Some(msg) => write!(f, "Error(\"{}\")", msg),
57-
None => write!(f, "Error(0x{:08X})", self.0),
93+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94+
let mut dbg = f.debug_struct("Error");
95+
match self.try_os_error() {
96+
Ok(errno) => {
97+
dbg.field("os_error", &errno);
98+
let mut buf = [0u8; 128];
99+
if let Some(desc) = os_err_desc(errno, &mut buf) {
100+
dbg.field("description", &desc);
101+
}
102+
}
103+
Err((0, code)) => {
104+
dbg.field("internal_code", &code);
105+
if let Some(desc) = internal_desc(code) {
106+
dbg.field("description", &desc);
107+
}
108+
}
109+
Err((block, code)) => {
110+
dbg.field("block", &block);
111+
dbg.field("custom_code", &code);
112+
}
58113
}
114+
dbg.finish()
59115
}
60116
}
61117

62118
impl fmt::Display for Error {
63-
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
64-
match self.msg() {
65-
Some(msg) => write!(f, "{}", msg),
66-
None => write!(f, "getrandom: unknown code 0x{:08X}", self.0),
119+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120+
match self.try_os_error() {
121+
Ok(errno) => {
122+
let mut buf = [0u8; 128];
123+
match os_err_desc(errno, &mut buf) {
124+
Some(desc) => f.write_str(desc),
125+
None => write!(f, "OS Error: {}", errno),
126+
}
127+
}
128+
Err((0, code)) => match internal_desc(code) {
129+
Some(desc) => f.write_str(desc),
130+
None => write!(f, "Internal Error: {}", code),
131+
},
132+
Err((block, code)) => write!(f, "Custom Error: block={}, code={}", block, code),
67133
}
68134
}
69135
}
70136

71137
impl From<NonZeroU32> for Error {
72138
fn from(code: NonZeroU32) -> Self {
73-
Error(code)
139+
Self(code)
74140
}
75141
}
76142

77-
impl From<&Error> for Error {
78-
fn from(error: &Error) -> Self {
79-
*error
143+
/// Internal Error constants
144+
pub(crate) const UNSUPPORTED: u16 = 0;
145+
pub(crate) const UNKNOWN_IO_ERROR: u16 = 1;
146+
pub(crate) const SEC_RANDOM_FAILED: u16 = 2;
147+
pub(crate) const RTL_GEN_RANDOM_FAILED: u16 = 3;
148+
pub(crate) const FAILED_RDRAND: u16 = 4;
149+
pub(crate) const NO_RDRAND: u16 = 5;
150+
pub(crate) const BINDGEN_CRYPTO_UNDEF: u16 = 6;
151+
pub(crate) const BINDGEN_GRV_UNDEF: u16 = 7;
152+
pub(crate) const STDWEB_NO_RNG: u16 = 8;
153+
pub(crate) const STDWEB_RNG_FAILED: u16 = 9;
154+
155+
fn internal_desc(code: u16) -> Option<&'static str> {
156+
match code {
157+
UNSUPPORTED => Some("getrandom: this target is not supported"),
158+
UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"),
159+
SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"),
160+
RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"),
161+
FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
162+
NO_RDRAND => Some("RDRAND: instruction not supported"),
163+
BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"),
164+
BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"),
165+
STDWEB_NO_RNG => Some("stdweb: no randomness source available"),
166+
STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"),
167+
_ => None,
80168
}
81169
}
82170

src/error_impls.rs

+10-12
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,26 @@
77
// except according to those terms.
88
extern crate std;
99

10-
use crate::error::Error;
10+
use crate::{error::UNKNOWN_IO_ERROR, Error};
1111
use core::convert::From;
12-
use core::num::NonZeroU32;
13-
use std::{error, io};
12+
use std::io;
1413

1514
impl From<io::Error> for Error {
1615
fn from(err: io::Error) -> Self {
17-
err.raw_os_error()
18-
.and_then(|code| NonZeroU32::new(code as u32))
19-
.map(|code| Error(code))
20-
// in practice this should never happen
21-
.unwrap_or(Error::UNKNOWN)
16+
match err.raw_os_error() {
17+
Some(errno) => Self::from_os_error(errno),
18+
None => Self::internal(UNKNOWN_IO_ERROR),
19+
}
2220
}
2321
}
2422

2523
impl From<Error> for io::Error {
2624
fn from(err: Error) -> Self {
27-
match err.msg() {
28-
Some(msg) => io::Error::new(io::ErrorKind::Other, msg),
29-
None => io::Error::from_raw_os_error(err.0.get() as i32),
25+
match err.raw_os_error() {
26+
Some(errno) => io::Error::from_raw_os_error(errno),
27+
None => io::Error::new(io::ErrorKind::Other, err),
3028
}
3129
}
3230
}
3331

34-
impl error::Error for Error {}
32+
impl std::error::Error for Error {}

src/freebsd.rs

-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
//! Implementation for FreeBSD
1010
use crate::util_libc::{sys_fill_exact, Weak};
1111
use crate::Error;
12-
use core::num::NonZeroU32;
1312
use core::{mem, ptr};
1413

1514
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> {
4443
sys_fill_exact(dest, kern_arnd)
4544
}
4645
}
47-
48-
#[inline(always)]
49-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
50-
None
51-
}

src/fuchsia.rs

-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
//! Implementation for Fuchsia Zircon
1010
use crate::Error;
11-
use core::num::NonZeroU32;
1211

1312
#[link(name = "zircon")]
1413
extern "C" {
@@ -19,8 +18,3 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1918
unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) }
2019
Ok(())
2120
}
22-
23-
#[inline(always)]
24-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
25-
None
26-
}

src/ios.rs

+2-11
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
// except according to those terms.
88

99
//! Implementation for iOS
10-
extern crate std;
11-
12-
use crate::Error;
13-
use core::num::NonZeroU32;
10+
use crate::{error::SEC_RANDOM_FAILED, Error};
1411

1512
// TODO: Make extern once extern_types feature is stabilized. See:
1613
// https://github.com/rust-lang/rust/issues/43467
@@ -27,14 +24,8 @@ extern "C" {
2724
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
2825
let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
2926
if ret == -1 {
30-
error!("SecRandomCopyBytes call failed");
31-
Err(Error::UNKNOWN)
27+
Err(Error::internal(SEC_RANDOM_FAILED))
3228
} else {
3329
Ok(())
3430
}
3531
}
36-
37-
#[inline(always)]
38-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
39-
None
40-
}

src/lib.rs

+2-20
Original file line numberDiff line numberDiff line change
@@ -79,25 +79,8 @@
7979
//! `getrandom`, hence after the first successful call one can be reasonably
8080
//! confident that no errors will occur.
8181
//!
82-
//! On unsupported platforms, `getrandom` always fails with [`Error::UNAVAILABLE`].
83-
//!
84-
//! ## Error codes
85-
//! The crate uses the following custom error codes:
86-
//! - `0x57f4c500` (dec: 1475659008) - an unknown error. Constant:
87-
//! [`Error::UNKNOWN`]
88-
//! - `0x57f4c501` (dec: 1475659009) - no generator is available. Constant:
89-
//! [`Error::UNAVAILABLE`]
90-
//! - `0x57f4c580` (dec: 1475659136) - `self.crypto` is undefined,
91-
//! `wasm-bindgen` specific error.
92-
//! - `0x57f4c581` (dec: 1475659137) - `crypto.getRandomValues` is undefined,
93-
//! `wasm-bindgen` specific error.
94-
//!
95-
//! These codes are provided for reference only and should not be matched upon
96-
//! (but you can match on `Error` constants). The codes may change in future and
97-
//! such change will not be considered a breaking one.
98-
//!
99-
//! Other error codes will originate from an underlying system. In case if such
100-
//! error is encountered, please consult with your system documentation.
82+
//! On unsupported platforms, `getrandom` always fails. See the [`Error`] type
83+
//! for more information on what data is returned on failure.
10184
//!
10285
//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
10386
//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
@@ -197,7 +180,6 @@ mod error_impls;
197180
target_os = "solaris",
198181
target_os = "illumos",
199182
))]
200-
#[allow(dead_code)]
201183
mod use_file;
202184

203185
// System-specific implementations.

0 commit comments

Comments
 (0)