Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 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. |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 8 | use core::{fmt, num::NonZeroU32}; |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 9 | |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 10 | /// A small and `no_std` compatible error type |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 11 | /// |
| 12 | /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and |
| 13 | /// if so, which error code the OS gave the application. If such an error is |
| 14 | /// encountered, please consult with your system documentation. |
| 15 | /// |
| 16 | /// Internally this type is a NonZeroU32, with certain values reserved for |
| 17 | /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 18 | /// |
| 19 | /// *If this crate's `"std"` Cargo feature is enabled*, then: |
| 20 | /// - [`getrandom::Error`][Error] implements |
| 21 | /// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) |
| 22 | /// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements |
| 23 | /// [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html). |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 24 | #[derive(Copy, Clone, Eq, PartialEq)] |
| 25 | pub struct Error(NonZeroU32); |
| 26 | |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 27 | const fn internal_error(n: u16) -> Error { |
| 28 | // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32. |
| 29 | let code = Error::INTERNAL_START + (n as u32); |
| 30 | Error(unsafe { NonZeroU32::new_unchecked(code) }) |
| 31 | } |
| 32 | |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 33 | impl Error { |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 34 | /// This target/platform is not supported by `getrandom`. |
| 35 | pub const UNSUPPORTED: Error = internal_error(0); |
| 36 | /// The platform-specific `errno` returned a non-positive value. |
| 37 | pub const ERRNO_NOT_POSITIVE: Error = internal_error(1); |
| 38 | /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed. |
| 39 | pub const IOS_SEC_RANDOM: Error = internal_error(3); |
| 40 | /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. |
| 41 | pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4); |
| 42 | /// RDRAND instruction failed due to a hardware issue. |
| 43 | pub const FAILED_RDRAND: Error = internal_error(5); |
| 44 | /// RDRAND instruction unsupported on this target. |
| 45 | pub const NO_RDRAND: Error = internal_error(6); |
| 46 | /// The browser does not have support for `self.crypto`. |
| 47 | pub const WEB_CRYPTO: Error = internal_error(7); |
| 48 | /// The browser does not have support for `crypto.getRandomValues`. |
| 49 | pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8); |
| 50 | /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized). |
| 51 | pub const VXWORKS_RAND_SECURE: Error = internal_error(11); |
| 52 | /// NodeJS does not have support for the `crypto` module. |
| 53 | pub const NODE_CRYPTO: Error = internal_error(12); |
| 54 | /// NodeJS does not have support for `crypto.randomFillSync`. |
| 55 | pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13); |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 56 | |
| 57 | /// Codes below this point represent OS Errors (i.e. positive i32 values). |
| 58 | /// Codes at or above this point, but below [`Error::CUSTOM_START`] are |
| 59 | /// reserved for use by the `rand` and `getrandom` crates. |
| 60 | pub const INTERNAL_START: u32 = 1 << 31; |
| 61 | |
| 62 | /// Codes at or above this point can be used by users to define their own |
| 63 | /// custom errors. |
| 64 | pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); |
| 65 | |
| 66 | /// Extract the raw OS error code (if this error came from the OS) |
| 67 | /// |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 68 | /// This method is identical to [`std::io::Error::raw_os_error()`][1], except |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 69 | /// that it works in `no_std` contexts. If this method returns `None`, the |
| 70 | /// error value can still be formatted via the `Display` implementation. |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 71 | /// |
| 72 | /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 73 | #[inline] |
| 74 | pub fn raw_os_error(self) -> Option<i32> { |
| 75 | if self.0.get() < Self::INTERNAL_START { |
David LeGare | 010ab1d | 2022-03-02 16:21:18 +0000 | [diff] [blame] | 76 | match () { |
| 77 | #[cfg(target_os = "solid_asp3")] |
| 78 | // On SOLID, negate the error code again to obtain the original |
| 79 | // error code. |
| 80 | () => Some(-(self.0.get() as i32)), |
| 81 | #[cfg(not(target_os = "solid_asp3"))] |
| 82 | () => Some(self.0.get() as i32), |
| 83 | } |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 84 | } else { |
| 85 | None |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /// Extract the bare error code. |
| 90 | /// |
| 91 | /// This code can either come from the underlying OS, or be a custom error. |
| 92 | /// Use [`Error::raw_os_error()`] to disambiguate. |
| 93 | #[inline] |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 94 | pub const fn code(self) -> NonZeroU32 { |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 95 | self.0 |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | cfg_if! { |
| 100 | if #[cfg(unix)] { |
| 101 | fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> { |
| 102 | let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; |
| 103 | if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { |
| 104 | return None; |
| 105 | } |
| 106 | |
| 107 | // Take up to trailing null byte |
| 108 | let n = buf.len(); |
| 109 | let idx = buf.iter().position(|&b| b == 0).unwrap_or(n); |
| 110 | core::str::from_utf8(&buf[..idx]).ok() |
| 111 | } |
| 112 | } else if #[cfg(target_os = "wasi")] { |
| 113 | fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> { |
| 114 | wasi::Error::from_raw_error(errno as _) |
| 115 | } |
| 116 | } else { |
| 117 | fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> { |
| 118 | None |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | impl fmt::Debug for Error { |
| 124 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 125 | let mut dbg = f.debug_struct("Error"); |
| 126 | if let Some(errno) = self.raw_os_error() { |
| 127 | dbg.field("os_error", &errno); |
| 128 | let mut buf = [0u8; 128]; |
| 129 | if let Some(err) = os_err(errno, &mut buf) { |
| 130 | dbg.field("description", &err); |
| 131 | } |
| 132 | } else if let Some(desc) = internal_desc(*self) { |
| 133 | dbg.field("internal_code", &self.0.get()); |
| 134 | dbg.field("description", &desc); |
| 135 | } else { |
| 136 | dbg.field("unknown_code", &self.0.get()); |
| 137 | } |
| 138 | dbg.finish() |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | impl fmt::Display for Error { |
| 143 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 144 | if let Some(errno) = self.raw_os_error() { |
| 145 | let mut buf = [0u8; 128]; |
| 146 | match os_err(errno, &mut buf) { |
| 147 | Some(err) => err.fmt(f), |
| 148 | None => write!(f, "OS Error: {}", errno), |
| 149 | } |
| 150 | } else if let Some(desc) = internal_desc(*self) { |
| 151 | f.write_str(desc) |
| 152 | } else { |
| 153 | write!(f, "Unknown Error: {}", self.0.get()) |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | impl From<NonZeroU32> for Error { |
| 159 | fn from(code: NonZeroU32) -> Self { |
| 160 | Self(code) |
| 161 | } |
| 162 | } |
| 163 | |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 164 | fn internal_desc(error: Error) -> Option<&'static str> { |
| 165 | match error { |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 166 | Error::UNSUPPORTED => Some("getrandom: this target is not supported"), |
| 167 | Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), |
| 168 | Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"), |
| 169 | Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"), |
| 170 | Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), |
| 171 | Error::NO_RDRAND => Some("RDRAND: instruction not supported"), |
Joel Galenson | df76097 | 2021-06-21 12:36:13 -0700 | [diff] [blame] | 172 | Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"), |
Thiébaud Weksteen | 9791b30 | 2021-03-03 16:30:20 +0100 | [diff] [blame] | 173 | Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"), |
| 174 | Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"), |
| 175 | Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"), |
| 176 | Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"), |
Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 177 | _ => None, |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | #[cfg(test)] |
| 182 | mod tests { |
| 183 | use super::Error; |
| 184 | use core::mem::size_of; |
| 185 | |
| 186 | #[test] |
| 187 | fn test_size() { |
| 188 | assert_eq!(size_of::<Error>(), 4); |
| 189 | assert_eq!(size_of::<Result<(), Error>>(), 4); |
| 190 | } |
| 191 | } |