Coverage Report

Created: 2024-04-26 06:25

/rust/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.10/src/error.rs
Line
Count
Source (jump to first uncovered line)
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, num::NonZeroU32};
9
10
/// A small and `no_std` compatible error type
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`].
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).
24
0
#[derive(Copy, Clone, Eq, PartialEq)]
25
pub struct Error(NonZeroU32);
26
27
0
const fn internal_error(n: u16) -> Error {
28
0
    // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
29
0
    let code = Error::INTERNAL_START + (n as u32);
30
0
    Error(unsafe { NonZeroU32::new_unchecked(code) })
31
0
}
32
33
impl Error {
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 environment does not support the Web Crypto API.
47
    pub const WEB_CRYPTO: Error = internal_error(7);
48
    /// Calling Web Crypto API `crypto.getRandomValues` failed.
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
    /// Node.js does not have the `crypto` CommonJS module.
53
    pub const NODE_CRYPTO: Error = internal_error(12);
54
    /// Calling Node.js function `crypto.randomFillSync` failed.
55
    pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
56
    /// Called from an ES module on Node.js. This is unsupported, see:
57
    /// <https://docs.rs/getrandom#nodejs-es-module-support>.
58
    pub const NODE_ES_MODULE: Error = internal_error(14);
59
60
    /// Codes below this point represent OS Errors (i.e. positive i32 values).
61
    /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
62
    /// reserved for use by the `rand` and `getrandom` crates.
63
    pub const INTERNAL_START: u32 = 1 << 31;
64
65
    /// Codes at or above this point can be used by users to define their own
66
    /// custom errors.
67
    pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
68
69
    /// Extract the raw OS error code (if this error came from the OS)
70
    ///
71
    /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
72
    /// that it works in `no_std` contexts. If this method returns `None`, the
73
    /// error value can still be formatted via the `Display` implementation.
74
    ///
75
    /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
76
    #[inline]
77
0
    pub fn raw_os_error(self) -> Option<i32> {
78
0
        if self.0.get() < Self::INTERNAL_START {
79
0
            match () {
80
0
                #[cfg(target_os = "solid_asp3")]
81
0
                // On SOLID, negate the error code again to obtain the original
82
0
                // error code.
83
0
                () => Some(-(self.0.get() as i32)),
84
0
                #[cfg(not(target_os = "solid_asp3"))]
85
0
                () => Some(self.0.get() as i32),
86
            }
87
        } else {
88
0
            None
89
        }
90
0
    }
91
92
    /// Extract the bare error code.
93
    ///
94
    /// This code can either come from the underlying OS, or be a custom error.
95
    /// Use [`Error::raw_os_error()`] to disambiguate.
96
    #[inline]
97
0
    pub const fn code(self) -> NonZeroU32 {
98
0
        self.0
99
0
    }
100
}
101
102
cfg_if! {
103
    if #[cfg(unix)] {
104
0
        fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> {
105
0
            let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
106
0
            if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
107
0
                return None;
108
0
            }
109
0
110
0
            // Take up to trailing null byte
111
0
            let n = buf.len();
112
0
            let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
113
0
            core::str::from_utf8(&buf[..idx]).ok()
114
0
        }
115
    } else {
116
        fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
117
            None
118
        }
119
    }
120
}
121
122
impl fmt::Debug for Error {
123
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124
0
        let mut dbg = f.debug_struct("Error");
125
0
        if let Some(errno) = self.raw_os_error() {
126
0
            dbg.field("os_error", &errno);
127
0
            let mut buf = [0u8; 128];
128
0
            if let Some(err) = os_err(errno, &mut buf) {
129
0
                dbg.field("description", &err);
130
0
            }
131
0
        } else if let Some(desc) = internal_desc(*self) {
132
0
            dbg.field("internal_code", &self.0.get());
133
0
            dbg.field("description", &desc);
134
0
        } else {
135
0
            dbg.field("unknown_code", &self.0.get());
136
0
        }
137
0
        dbg.finish()
138
0
    }
139
}
140
141
impl fmt::Display for Error {
142
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143
0
        if let Some(errno) = self.raw_os_error() {
144
0
            let mut buf = [0u8; 128];
145
0
            match os_err(errno, &mut buf) {
146
0
                Some(err) => err.fmt(f),
147
0
                None => write!(f, "OS Error: {}", errno),
148
            }
149
0
        } else if let Some(desc) = internal_desc(*self) {
150
0
            f.write_str(desc)
151
        } else {
152
0
            write!(f, "Unknown Error: {}", self.0.get())
153
        }
154
0
    }
155
}
156
157
impl From<NonZeroU32> for Error {
158
0
    fn from(code: NonZeroU32) -> Self {
159
0
        Self(code)
160
0
    }
161
}
162
163
0
fn internal_desc(error: Error) -> Option<&'static str> {
164
0
    match error {
165
0
        Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
166
0
        Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
167
0
        Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
168
0
        Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
169
0
        Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
170
0
        Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
171
0
        Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"),
172
0
        Error::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"),
173
0
        Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
174
0
        Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"),
175
0
        Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
176
0
        Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"),
177
0
        _ => None,
178
    }
179
0
}
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
}