Coverage Report

Created: 2025-07-11 06:22

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