1 // Copyright 2018 Developers of the Rand project.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 use core::fmt;
9 use core::num::NonZeroU32;
10 
11 /// A small and `no_std` compatible error type.
12 ///
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.
16 ///
17 /// Internally this type is a NonZeroU32, with certain values reserved for
18 /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
19 #[derive(Copy, Clone, Eq, PartialEq)]
20 pub struct Error(NonZeroU32);
21 
22 impl Error {
23     #[deprecated(since = "0.1.7")]
24     /// Unknown error.
25     pub const UNKNOWN: Error = UNSUPPORTED;
26     #[deprecated(since = "0.1.7")]
27     /// System entropy source is unavailable.
28     pub const UNAVAILABLE: Error = UNSUPPORTED;
29 
30     /// Codes below this point represent OS Errors (i.e. positive i32 values).
31     /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
32     /// reserved for use by the `rand` and `getrandom` crates.
33     pub const INTERNAL_START: u32 = 1 << 31;
34 
35     /// Codes at or above this point can be used by users to define their own
36     /// custom errors.
37     pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
38 
39     /// Extract the raw OS error code (if this error came from the OS)
40     ///
41     /// This method is identical to `std::io::Error::raw_os_error()`, except
42     /// that it works in `no_std` contexts. If this method returns `None`, the
43     /// error value can still be formatted via the `Display` implementation.
44     #[inline]
raw_os_error(self) -> Option<i32>45     pub fn raw_os_error(self) -> Option<i32> {
46         if self.0.get() < Self::INTERNAL_START {
47             Some(self.0.get() as i32)
48         } else {
49             None
50         }
51     }
52 
53     /// Extract the bare error code.
54     ///
55     /// This code can either come from the underlying OS, or be a custom error.
56     /// Use [`Error::raw_os_error()`] to disambiguate.
57     #[inline]
code(self) -> NonZeroU3258     pub fn code(self) -> NonZeroU32 {
59         self.0
60     }
61 }
62 
63 cfg_if! {
64     if #[cfg(unix)] {
65         fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> {
66             let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
67             if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
68                 return None;
69             }
70 
71             // Take up to trailing null byte
72             let n = buf.len();
73             let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
74             core::str::from_utf8(&buf[..idx]).ok()
75         }
76     } else if #[cfg(target_os = "wasi")] {
77         fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> {
78             wasi::Error::from_raw_error(errno as _)
79         }
80     } else {
81         fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
82             None
83         }
84     }
85 }
86 
87 impl fmt::Debug for Error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result88     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89         let mut dbg = f.debug_struct("Error");
90         if let Some(errno) = self.raw_os_error() {
91             dbg.field("os_error", &errno);
92             let mut buf = [0u8; 128];
93             if let Some(err) = os_err(errno, &mut buf) {
94                 dbg.field("description", &err);
95             }
96         } else if let Some(desc) = internal_desc(*self) {
97             dbg.field("internal_code", &self.0.get());
98             dbg.field("description", &desc);
99         } else {
100             dbg.field("unknown_code", &self.0.get());
101         }
102         dbg.finish()
103     }
104 }
105 
106 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result107     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108         if let Some(errno) = self.raw_os_error() {
109             let mut buf = [0u8; 128];
110             match os_err(errno, &mut buf) {
111                 Some(err) => err.fmt(f),
112                 None => write!(f, "OS Error: {}", errno),
113             }
114         } else if let Some(desc) = internal_desc(*self) {
115             f.write_str(desc)
116         } else {
117             write!(f, "Unknown Error: {}", self.0.get())
118         }
119     }
120 }
121 
122 impl From<NonZeroU32> for Error {
from(code: NonZeroU32) -> Self123     fn from(code: NonZeroU32) -> Self {
124         Self(code)
125     }
126 }
127 
128 // TODO: Convert to a function when min_version >= 1.33
129 macro_rules! internal_error {
130     ($n:expr) => {
131         Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + $n as u16 as u32) })
132     };
133 }
134 
135 /// Internal Error constants
136 pub(crate) const UNSUPPORTED: Error = internal_error!(0);
137 pub(crate) const ERRNO_NOT_POSITIVE: Error = internal_error!(1);
138 pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error!(2);
139 pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3);
140 pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4);
141 pub(crate) const FAILED_RDRAND: Error = internal_error!(5);
142 pub(crate) const NO_RDRAND: Error = internal_error!(6);
143 pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7);
144 pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8);
145 pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9);
146 pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10);
147 pub(crate) const RAND_SECURE_FATAL: Error = internal_error!(11);
148 
internal_desc(error: Error) -> Option<&'static str>149 fn internal_desc(error: Error) -> Option<&'static str> {
150     match error {
151         UNSUPPORTED => Some("getrandom: this target is not supported"),
152         ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
153         UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"),
154         SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"),
155         RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"),
156         FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
157         NO_RDRAND => Some("RDRAND: instruction not supported"),
158         BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"),
159         BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"),
160         STDWEB_NO_RNG => Some("stdweb: no randomness source available"),
161         STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"),
162         RAND_SECURE_FATAL => Some("randSecure: random number generator module is not initialized"),
163         _ => None,
164     }
165 }
166 
167 #[cfg(test)]
168 mod tests {
169     use super::Error;
170     use core::mem::size_of;
171 
172     #[test]
test_size()173     fn test_size() {
174         assert_eq!(size_of::<Error>(), 4);
175         assert_eq!(size_of::<Result<(), Error>>(), 4);
176     }
177 }
178