Skip to content

Commit

Permalink
Make target-specific errors private (#562)
Browse files Browse the repository at this point in the history
The main reason for existence of the target-specific errors is nicer
error descriptions. Users arguably should not match on those error
constants since our inner implementation may change together with
possible error codes (for example, like with `IOS_SEC_RANDOM`). Marking
the error constants as private we get flexibility to change error codes
and names.

Additionally, the `internal_desc` change reduces generated binary size
since compiler no longer has to include irrelevant error descriptions.
Ideally the target-specific error descriptions would be defined in the
relevant modules, but I couldn't find a good way to do it, so,
unfortunately, I had to partially duplicate backend `cfg`s in
`internal_desc`.
  • Loading branch information
newpavlov authored Dec 11, 2024
1 parent 30a7f1f commit 83bef12
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 43 deletions.
7 changes: 6 additions & 1 deletion src/backends/apple_other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
if ret == libc::kCCSuccess {
Ok(())
} else {
Err(Error::IOS_SEC_RANDOM)
Err(Error::IOS_RANDOM_GEN)
}
}

impl Error {
/// Call to `CCRandomGenerateBytes` failed.
pub(crate) const IOS_RANDOM_GEN: Error = Self::new_internal(10);
}
7 changes: 7 additions & 0 deletions src/backends/rdrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,10 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// SAFETY: After this point, we know rdrand is supported.
unsafe { rdrand_exact(dest) }.ok_or(Error::FAILED_RDRAND)
}

impl Error {
/// RDRAND instruction failed due to a hardware issue.
pub(crate) const FAILED_RDRAND: Error = Self::new_internal(10);
/// RDRAND instruction unsupported on this target.
pub(crate) const NO_RDRAND: Error = Self::new_internal(11);
}
7 changes: 7 additions & 0 deletions src/backends/rndr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,10 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
Err(Error::RNDR_NOT_AVAILABLE)
}
}

impl Error {
/// RNDR register read failed due to a hardware issue.
pub(crate) const RNDR_FAILURE: Error = Self::new_internal(10);
/// RNDR register is not supported on this target.
pub(crate) const RNDR_NOT_AVAILABLE: Error = Self::new_internal(11);
}
5 changes: 5 additions & 0 deletions src/backends/vxworks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,8 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
}
Ok(())
}

impl Error {
/// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
pub(crate) const VXWORKS_RAND_SECURE: Error = Self::new_internal(10);
}
5 changes: 5 additions & 0 deletions src/backends/wasm_js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ extern "C" {
#[wasm_bindgen(js_namespace = ["globalThis", "crypto"], js_name = getRandomValues, catch)]
fn get_random_values(buf: &js_sys::Uint8Array) -> Result<(), JsValue>;
}

impl Error {
/// The environment does not support the Web Crypto API.
pub(crate) const WEB_CRYPTO: Error = Self::new_internal(10);
}
5 changes: 5 additions & 0 deletions src/backends/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
_ => Err(Error::WINDOWS_PROCESS_PRNG),
}
}

impl Error {
/// Calling Windows ProcessPrng failed.
pub(crate) const WINDOWS_PROCESS_PRNG: Error = Self::new_internal(10);
}
5 changes: 5 additions & 0 deletions src/backends/windows7.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
}
Ok(())
}

impl Error {
/// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
pub(crate) const WINDOWS_RTL_GEN_RANDOM: Error = Self::new_internal(10);
}
86 changes: 44 additions & 42 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,6 @@ impl Error {
pub const ERRNO_NOT_POSITIVE: Error = Self::new_internal(1);
/// Encountered an unexpected situation which should not happen in practice.
pub const UNEXPECTED: Error = Self::new_internal(2);
/// Call to [`CCRandomGenerateBytes`](https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html) failed
/// on iOS, tvOS, or waatchOS.
// TODO: Update this constant name in the next breaking release.
pub const IOS_SEC_RANDOM: Error = Self::new_internal(3);
/// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
pub const WINDOWS_RTL_GEN_RANDOM: Error = Self::new_internal(4);
/// RDRAND instruction failed due to a hardware issue.
pub const FAILED_RDRAND: Error = Self::new_internal(5);
/// RDRAND instruction unsupported on this target.
pub const NO_RDRAND: Error = Self::new_internal(6);
/// The environment does not support the Web Crypto API.
pub const WEB_CRYPTO: Error = Self::new_internal(7);
/// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
pub const VXWORKS_RAND_SECURE: Error = Self::new_internal(11);
/// Calling Windows ProcessPrng failed.
pub const WINDOWS_PROCESS_PRNG: Error = Self::new_internal(12);
/// RNDR register read failed due to a hardware issue.
pub const RNDR_FAILURE: Error = Self::new_internal(13);
/// RNDR register is not supported on this target.
pub const RNDR_NOT_AVAILABLE: Error = Self::new_internal(14);

/// Codes below this point represent OS Errors (i.e. positive i32 values).
/// Codes at or above this point, but below [`Error::CUSTOM_START`] are
Expand Down Expand Up @@ -101,11 +81,52 @@ impl Error {
}

/// Creates a new instance of an `Error` from a particular internal error code.
const fn new_internal(n: u16) -> Error {
pub(crate) const fn new_internal(n: u16) -> Error {
// SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
let code = Error::INTERNAL_START + (n as u32);
Error(unsafe { NonZeroU32::new_unchecked(code) })
}

fn internal_desc(&self) -> Option<&'static str> {
let desc = match *self {
Error::UNSUPPORTED => "getrandom: this target is not supported",
Error::ERRNO_NOT_POSITIVE => "errno: did not return a positive value",
Error::UNEXPECTED => "unexpected situation",
#[cfg(any(
target_os = "ios",
target_os = "visionos",
target_os = "watchos",
target_os = "tvos",
))]
Error::IOS_RANDOM_GEN => "SecRandomCopyBytes: iOS Security framework failure",
#[cfg(all(windows, not(target_vendor = "win7")))]
Error::WINDOWS_PROCESS_PRNG => "ProcessPrng: Windows system function failure",
#[cfg(all(windows, target_vendor = "win7"))]
Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure",
#[cfg(getrandom_backend = "wasm_js")]
Error::WEB_CRYPTO => "Web Crypto API is unavailable",
#[cfg(target_os = "vxworks")]
Error::VXWORKS_RAND_SECURE => "randSecure: VxWorks RNG module is not initialized",

#[cfg(any(
getrandom_backend = "rdrand",
all(target_arch = "x86_64", target_env = "sgx")
))]
Error::FAILED_RDRAND => "RDRAND: failed multiple times: CPU issue likely",
#[cfg(any(
getrandom_backend = "rdrand",
all(target_arch = "x86_64", target_env = "sgx")
))]
Error::NO_RDRAND => "RDRAND: instruction not supported",

#[cfg(getrandom_backend = "rndr")]
Error::RNDR_FAILURE => "RNDR: Could not generate a random number",
#[cfg(getrandom_backend = "rndr")]
Error::RNDR_NOT_AVAILABLE => "RNDR: Register not supported",
_ => return None,
};
Some(desc)
}
}

impl fmt::Debug for Error {
Expand All @@ -115,7 +136,7 @@ impl fmt::Debug for Error {
dbg.field("os_error", &errno);
#[cfg(feature = "std")]
dbg.field("description", &std::io::Error::from_raw_os_error(errno));
} else if let Some(desc) = internal_desc(*self) {
} else if let Some(desc) = self.internal_desc() {
dbg.field("internal_code", &self.0.get());
dbg.field("description", &desc);
} else {
Expand All @@ -135,33 +156,14 @@ impl fmt::Display for Error {
write!(f, "OS Error: {}", errno)
}
}
} else if let Some(desc) = internal_desc(*self) {
} else if let Some(desc) = self.internal_desc() {
f.write_str(desc)
} else {
write!(f, "Unknown Error: {}", self.0.get())
}
}
}

fn internal_desc(error: Error) -> Option<&'static str> {
let desc = match error {
Error::UNSUPPORTED => "getrandom: this target is not supported",
Error::ERRNO_NOT_POSITIVE => "errno: did not return a positive value",
Error::UNEXPECTED => "unexpected situation",
Error::IOS_SEC_RANDOM => "SecRandomCopyBytes: iOS Security framework failure",
Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure",
Error::FAILED_RDRAND => "RDRAND: failed multiple times: CPU issue likely",
Error::NO_RDRAND => "RDRAND: instruction not supported",
Error::WEB_CRYPTO => "Web Crypto API is unavailable",
Error::VXWORKS_RAND_SECURE => "randSecure: VxWorks RNG module is not initialized",
Error::WINDOWS_PROCESS_PRNG => "ProcessPrng: Windows system function failure",
Error::RNDR_FAILURE => "RNDR: Could not generate a random number",
Error::RNDR_NOT_AVAILABLE => "RNDR: Register not supported",
_ => return None,
};
Some(desc)
}

#[cfg(test)]
mod tests {
use super::Error;
Expand Down

0 comments on commit 83bef12

Please sign in to comment.