Coverage Report

Created: 2026-02-26 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.4.1/src/error.rs
Line
Count
Source
1
#[cfg(feature = "std")]
2
extern crate std;
3
4
use core::fmt;
5
6
cfg_if::cfg_if!(
7
    if #[cfg(target_os = "uefi")] {
8
        // See the UEFI spec for more information:
9
        // https://uefi.org/specs/UEFI/2.10/Apx_D_Status_Codes.html
10
11
        /// Raw error code.
12
        ///
13
        /// This alias mirrors unstable [`std::io::RawOsError`].
14
        ///
15
        /// [`std::io::RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html
16
        pub type RawOsError = usize;
17
        type NonZeroRawOsError = core::num::NonZeroUsize;
18
        const UEFI_ERROR_FLAG: RawOsError = 1 << (RawOsError::BITS - 1);
19
    } else {
20
        /// Raw error code.
21
        ///
22
        /// This alias mirrors unstable [`std::io::RawOsError`].
23
        ///
24
        /// [`std::io::RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html
25
        pub type RawOsError = i32;
26
        type NonZeroRawOsError = core::num::NonZeroI32;
27
    }
28
);
29
30
/// A small and `no_std` compatible error type
31
///
32
/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
33
/// if so, which error code the OS gave the application. If such an error is
34
/// encountered, please consult with your system documentation.
35
///
36
/// *If this crate's `"std"` Cargo feature is enabled*, then:
37
/// - [`getrandom::Error`][Error] implements
38
///   [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
39
/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
40
///   [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
41
42
// note: on non-UEFI targets OS errors are represented as negative integers,
43
// while on UEFI targets OS errors have the highest bit set to 1.
44
#[derive(Copy, Clone, Eq, PartialEq)]
45
pub struct Error(NonZeroRawOsError);
46
47
impl Error {
48
    /// This target/platform is not supported by `getrandom`.
49
    pub const UNSUPPORTED: Error = Self::new_internal(0);
50
    /// The platform-specific `errno` returned a non-positive value.
51
    pub const ERRNO_NOT_POSITIVE: Error = Self::new_internal(1);
52
    /// Encountered an unexpected situation which should not happen in practice.
53
    pub const UNEXPECTED: Error = Self::new_internal(2);
54
55
    /// Internal errors can be in the range of 2^16..2^17
56
    const INTERNAL_START: RawOsError = 1 << 16;
57
    /// Custom errors can be in the range of 2^17..(2^17 + 2^16)
58
    const CUSTOM_START: RawOsError = 1 << 17;
59
60
    /// Creates a new `Error` instance from a positive error code.
61
    ///
62
    /// Returns [`Error::ERRNO_NOT_POSITIVE`] for zero and negative error codes.
63
    #[cfg(not(target_os = "uefi"))]
64
    #[allow(dead_code)]
65
0
    pub(super) fn from_errno(errno: i32) -> Self {
66
0
        if errno > 0 {
67
0
            let code = errno
68
0
                .checked_neg()
69
0
                .expect("Positive number can be always negated");
70
0
            Error::from_neg_error_code(code)
71
        } else {
72
0
            Error::ERRNO_NOT_POSITIVE
73
        }
74
0
    }
75
76
    /// Creates a new `Error` instance from a negative error code.
77
    ///
78
    /// Returns [`Error::UNEXPECTED`] for zero and positive error codes.
79
    #[cfg(not(target_os = "uefi"))]
80
    #[allow(dead_code)]
81
0
    pub(super) fn from_neg_error_code(code: RawOsError) -> Self {
82
0
        if code < 0 {
83
0
            let code = NonZeroRawOsError::new(code).expect("`code` is negative");
84
0
            Self(code)
85
        } else {
86
0
            Error::UNEXPECTED
87
        }
88
0
    }
89
90
    /// Creates a new instance of an `Error` from an UEFI error code.
91
    #[cfg(target_os = "uefi")]
92
    #[allow(dead_code)]
93
    pub(super) fn from_uefi_code(code: RawOsError) -> Self {
94
        if code & UEFI_ERROR_FLAG != 0 {
95
            let code = NonZeroRawOsError::new(code).expect("The highest bit of `code` is set to 1");
96
            Self(code)
97
        } else {
98
            Self::UNEXPECTED
99
        }
100
    }
101
102
    /// Extract the raw OS error code (if this error came from the OS)
103
    ///
104
    /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
105
    /// that it works in `no_std` contexts. On most targets this method returns
106
    /// `Option<i32>`, but some platforms (e.g. UEFI) may use a different primitive
107
    /// type like `usize`. Consult with the [`RawOsError`] docs for more information.
108
    ///
109
    /// If this method returns `None`, the error value can still be formatted via
110
    /// the `Display` implementation.
111
    ///
112
    /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
113
    /// [`RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html
114
    #[inline]
115
0
    pub fn raw_os_error(self) -> Option<RawOsError> {
116
0
        let code = self.0.get();
117
118
        // note: in this method we need to cover only backends which rely on
119
        // `Error::{from_error_code, from_errno, from_uefi_code}` methods,
120
        // on all other backends this method always returns `None`.
121
122
        #[cfg(target_os = "uefi")]
123
        {
124
            if code & UEFI_ERROR_FLAG != 0 {
125
                Some(code)
126
            } else {
127
                None
128
            }
129
        }
130
131
        #[cfg(not(target_os = "uefi"))]
132
        {
133
            // On most targets `std` expects positive error codes while retrieving error strings:
134
            // - `libc`-based targets use `strerror_r` which expects positive error codes.
135
            // - Hermit relies on the `hermit-abi` crate, which expects positive error codes:
136
            //   https://docs.rs/hermit-abi/0.4.0/src/hermit_abi/errno.rs.html#400-532
137
            // - WASIp1 uses the same conventions as `libc`:
138
            //   https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/wasi/os.rs#L57-L67
139
            //
140
            // The only exception is Solid, `std` expects negative system error codes, see:
141
            // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/solid/error.rs#L5-L31
142
0
            if code >= 0 {
143
0
                None
144
0
            } else if cfg!(not(target_os = "solid_asp3")) {
145
0
                code.checked_neg()
146
            } else {
147
0
                Some(code)
148
            }
149
        }
150
0
    }
151
152
    /// Creates a new instance of an `Error` from a particular custom error code.
153
0
    pub const fn new_custom(n: u16) -> Error {
154
        // SAFETY: code > 0 as CUSTOM_START > 0 and adding `n` won't overflow `RawOsError`.
155
0
        let code = Error::CUSTOM_START + (n as RawOsError);
156
0
        Error(unsafe { NonZeroRawOsError::new_unchecked(code) })
157
0
    }
158
159
    /// Creates a new instance of an `Error` from a particular internal error code.
160
0
    pub(crate) const fn new_internal(n: u16) -> Error {
161
        // SAFETY: code > 0 as INTERNAL_START > 0 and adding `n` won't overflow `RawOsError`.
162
0
        let code = Error::INTERNAL_START + (n as RawOsError);
163
0
        Error(unsafe { NonZeroRawOsError::new_unchecked(code) })
164
0
    }
165
166
0
    fn internal_desc(&self) -> Option<&'static str> {
167
0
        let desc = match *self {
168
0
            Error::UNSUPPORTED => "getrandom: this target is not supported",
169
0
            Error::ERRNO_NOT_POSITIVE => "errno: did not return a positive value",
170
0
            Error::UNEXPECTED => "unexpected situation",
171
            #[cfg(any(
172
                target_os = "ios",
173
                target_os = "visionos",
174
                target_os = "watchos",
175
                target_os = "tvos",
176
            ))]
177
            Error::IOS_RANDOM_GEN => "SecRandomCopyBytes: iOS Security framework failure",
178
            #[cfg(all(windows, target_vendor = "win7"))]
179
            Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure",
180
            #[cfg(all(
181
                feature = "wasm_js",
182
                target_arch = "wasm32",
183
                any(target_os = "unknown", target_os = "none")
184
            ))]
185
            Error::WEB_CRYPTO => "Web Crypto API is unavailable",
186
            #[cfg(target_os = "vxworks")]
187
            Error::VXWORKS_RAND_SECURE => "randSecure: VxWorks RNG module is not initialized",
188
189
            #[cfg(any(
190
                getrandom_backend = "rdrand",
191
                all(target_arch = "x86_64", target_env = "sgx")
192
            ))]
193
            Error::FAILED_RDRAND => "RDRAND: failed multiple times: CPU issue likely",
194
            #[cfg(any(
195
                getrandom_backend = "rdrand",
196
                all(target_arch = "x86_64", target_env = "sgx")
197
            ))]
198
            Error::NO_RDRAND => "RDRAND: instruction not supported",
199
200
            #[cfg(getrandom_backend = "rndr")]
201
            Error::RNDR_FAILURE => "RNDR: Could not generate a random number",
202
            #[cfg(getrandom_backend = "rndr")]
203
            Error::RNDR_NOT_AVAILABLE => "RNDR: Register not supported",
204
0
            _ => return None,
205
        };
206
0
        Some(desc)
207
0
    }
208
}
209
210
impl core::error::Error for Error {}
211
212
impl fmt::Debug for Error {
213
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214
0
        let mut dbg = f.debug_struct("Error");
215
0
        if let Some(errno) = self.raw_os_error() {
216
0
            dbg.field("os_error", &errno);
217
0
            #[cfg(feature = "std")]
218
0
            dbg.field("description", &std::io::Error::from_raw_os_error(errno));
219
0
        } else if let Some(desc) = self.internal_desc() {
220
0
            dbg.field("internal_code", &self.0.get());
221
0
            dbg.field("description", &desc);
222
0
        } else {
223
0
            dbg.field("unknown_code", &self.0.get());
224
0
        }
225
0
        dbg.finish()
226
0
    }
227
}
228
229
impl fmt::Display for Error {
230
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231
0
        if let Some(errno) = self.raw_os_error() {
232
            cfg_if! {
233
                if #[cfg(feature = "std")] {
234
                    std::io::Error::from_raw_os_error(errno).fmt(f)
235
                } else {
236
0
                    write!(f, "OS Error: {errno}")
237
                }
238
            }
239
0
        } else if let Some(desc) = self.internal_desc() {
240
0
            f.write_str(desc)
241
        } else {
242
0
            write!(f, "Unknown Error: {}", self.0.get())
243
        }
244
0
    }
245
}