From 61e398a97f5f376cbd6bb899b9b765deb7e12cf7 Mon Sep 17 00:00:00 2001 From: Hanyuan Li Date: Mon, 8 Sep 2025 15:24:38 +1000 Subject: [PATCH 1/6] feat: place vendored version of getrandom into rand/getrandom --- src/rand/getrandom.rs | 132 +++++++++++++++ src/rand/getrandom/backends.rs | 11 ++ src/rand/getrandom/backends/wasm_js.rs | 70 ++++++++ src/rand/getrandom/error.rs | 213 +++++++++++++++++++++++++ src/rand/getrandom/util.rs | 29 ++++ 5 files changed, 455 insertions(+) create mode 100644 src/rand/getrandom.rs create mode 100644 src/rand/getrandom/backends.rs create mode 100644 src/rand/getrandom/backends/wasm_js.rs create mode 100644 src/rand/getrandom/error.rs create mode 100644 src/rand/getrandom/util.rs diff --git a/src/rand/getrandom.rs b/src/rand/getrandom.rs new file mode 100644 index 0000000000..de38ccba6a --- /dev/null +++ b/src/rand/getrandom.rs @@ -0,0 +1,132 @@ +// This file, and all code in `rand/getrandom`, are vendored from +// the `getrandom` project: +// https://github.com/rust-random/getrandom/tree/78ef61a0e7a5dc876da0b496998464630a4c0bf0 +// +// Copyright (c) 2018-2025 The rust-random Project Developers +// Copyright (c) 2014 The Rust Project Developers +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// Overwrite links to crate items with intra-crate links +//! [`Error::UNEXPECTED`]: Error::UNEXPECTED +//! [`fill_uninit`]: fill_uninit + +#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![cfg_attr(getrandom_backend = "efi_rng", feature(uefi_std))] +#![deny( + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::cast_precision_loss, + clippy::cast_ptr_alignment, + clippy::cast_sign_loss, + clippy::char_lit_as_u8, + clippy::checked_conversions, + clippy::fn_to_numeric_cast, + clippy::fn_to_numeric_cast_with_truncation, + clippy::ptr_as_ptr, + clippy::unnecessary_cast, + clippy::useless_conversion +)] + +use core::mem::MaybeUninit; + +mod backends; +mod error; +mod util; + +#[cfg(feature = "std")] +mod error_std_impls; + +pub use error::Error; + +/// Fill `dest` with random bytes from the system's preferred random number source. +/// +/// This function returns an error on any failure, including partial reads. We +/// make no guarantees regarding the contents of `dest` on error. If `dest` is +/// empty, `getrandom` immediately returns success, making no calls to the +/// underlying operating system. +/// +/// Blocking is possible, at least during early boot; see module documentation. +/// +/// In general, `getrandom` will be fast enough for interactive usage, though +/// significantly slower than a user-space CSPRNG; for the latter consider +/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html). +/// +/// # Examples +/// +/// ``` +/// # fn main() -> Result<(), getrandom::Error> { +/// let mut buf = [0u8; 32]; +/// getrandom::fill(&mut buf)?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn fill(dest: &mut [u8]) -> Result<(), Error> { + // SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, + // and `fill_uninit` guarantees it will never de-initialize + // any part of `dest`. + let _ = fill_uninit(unsafe { util::slice_as_uninit_mut(dest) })?; + Ok(()) +} + +/// Fill potentially uninitialized buffer `dest` with random bytes from +/// the system's preferred random number source and return a mutable +/// reference to those bytes. +/// +/// On successful completion this function is guaranteed to return a slice +/// which points to the same memory as `dest` and has the same length. +/// In other words, it's safe to assume that `dest` is initialized after +/// this function has returned `Ok`. +/// +/// No part of `dest` will ever be de-initialized at any point, regardless +/// of what is returned. +/// +/// # Examples +/// +/// ```ignore +/// # // We ignore this test since `uninit_array` is unstable. +/// #![feature(maybe_uninit_uninit_array)] +/// # fn main() -> Result<(), getrandom::Error> { +/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>(); +/// let buf: &mut [u8] = getrandom::fill_uninit(&mut buf)?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<&mut [u8], Error> { + if !dest.is_empty() { + backends::fill_inner(dest)?; + } + + #[cfg(getrandom_msan)] + extern "C" { + fn __msan_unpoison(a: *mut core::ffi::c_void, size: usize); + } + + // SAFETY: `dest` has been fully initialized by `imp::fill_inner` + // since it returned `Ok`. + Ok(unsafe { util::slice_assume_init_mut(dest) }) +} diff --git a/src/rand/getrandom/backends.rs b/src/rand/getrandom/backends.rs new file mode 100644 index 0000000000..0296138d1b --- /dev/null +++ b/src/rand/getrandom/backends.rs @@ -0,0 +1,11 @@ +//! System-specific implementations. +//! +//! This module should provide `fill_inner` with the signature +//! `fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error>`. +//! The function MUST fully initialize `dest` when `Ok(())` is returned; +//! the function may need to use `sanitizer::unpoison` as well. +//! The function MUST NOT ever write uninitialized bytes into `dest`, +//! regardless of what value it returns. + +mod wasm_js; +pub use wasm_js::*; diff --git a/src/rand/getrandom/backends/wasm_js.rs b/src/rand/getrandom/backends/wasm_js.rs new file mode 100644 index 0000000000..8d99b83d61 --- /dev/null +++ b/src/rand/getrandom/backends/wasm_js.rs @@ -0,0 +1,70 @@ +//! Implementation for WASM based on Web and Node.js +use crate::rand::getrandom::Error; +use core::mem::MaybeUninit; + +#[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] +compile_error!("`wasm_js` backend can be enabled only for OS-less WASM targets!"); + +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; + +// Maximum buffer size allowed in `Crypto.getRandomValuesSize` is 65536 bytes. +// See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues +const MAX_BUFFER_SIZE: usize = 65536; + +#[cfg(not(target_feature = "atomics"))] +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + for chunk in dest.chunks_mut(MAX_BUFFER_SIZE) { + if get_random_values(chunk).is_err() { + return Err(Error::WEB_CRYPTO); + } + } + Ok(()) +} + +#[cfg(target_feature = "atomics")] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // getRandomValues does not work with all types of WASM memory, + // so we initially write to browser memory to avoid exceptions. + let buf_len = usize::min(dest.len(), MAX_BUFFER_SIZE); + let buf_len_u32 = buf_len + .try_into() + .expect("buffer length is bounded by MAX_BUFFER_SIZE"); + let buf = js_sys::Uint8Array::new_with_length(buf_len_u32); + for chunk in dest.chunks_mut(buf_len) { + let chunk_len = chunk + .len() + .try_into() + .expect("chunk length is bounded by MAX_BUFFER_SIZE"); + // The chunk can be smaller than buf's length, so we call to + // JS to create a smaller view of buf without allocation. + let sub_buf = if chunk_len == buf_len_u32 { + &buf + } else { + &buf.subarray(0, chunk_len) + }; + + if get_random_values(sub_buf).is_err() { + return Err(Error::WEB_CRYPTO); + } + + sub_buf.copy_to_uninit(chunk); + } + Ok(()) +} + +#[wasm_bindgen] +extern "C" { + // Crypto.getRandomValues() + #[cfg(not(target_feature = "atomics"))] + #[wasm_bindgen(js_namespace = ["globalThis", "crypto"], js_name = getRandomValues, catch)] + fn get_random_values(buf: &mut [MaybeUninit]) -> Result<(), JsValue>; + #[cfg(target_feature = "atomics")] + #[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); +} \ No newline at end of file diff --git a/src/rand/getrandom/error.rs b/src/rand/getrandom/error.rs new file mode 100644 index 0000000000..83ef78a44b --- /dev/null +++ b/src/rand/getrandom/error.rs @@ -0,0 +1,213 @@ +#[cfg(feature = "std")] +extern crate std; + +use cfg_if::cfg_if; +use core::fmt; + +// This private alias mirrors `std::io::RawOsError`: +// https://doc.rust-lang.org/std/io/type.RawOsError.html) +cfg_if::cfg_if!( + if #[cfg(target_os = "uefi")] { + // See the UEFI spec for more information: + // https://uefi.org/specs/UEFI/2.10/Apx_D_Status_Codes.html + type RawOsError = usize; + type NonZeroRawOsError = core::num::NonZeroUsize; + const UEFI_ERROR_FLAG: RawOsError = 1 << (RawOsError::BITS - 1); + } else { + type RawOsError = i32; + type NonZeroRawOsError = core::num::NonZeroI32; + } +); + +/// A small and `no_std` compatible error type +/// +/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and +/// if so, which error code the OS gave the application. If such an error is +/// encountered, please consult with your system documentation. +/// +/// *If this crate's `"std"` Cargo feature is enabled*, then: +/// - [`getrandom::Error`][Error] implements +/// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) +/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements +/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html). + +// note: on non-UEFI targets OS errors are represented as negative integers, +// while on UEFI targets OS errors have the highest bit set to 1. +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Error(NonZeroRawOsError); + +impl Error { + /// This target/platform is not supported by `getrandom`. + pub const UNSUPPORTED: Error = Self::new_internal(0); + /// The platform-specific `errno` returned a non-positive value. + 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); + + /// Internal errors can be in the range of 2^16..2^17 + const INTERNAL_START: RawOsError = 1 << 16; + /// Custom errors can be in the range of 2^17..(2^17 + 2^16) + const CUSTOM_START: RawOsError = 1 << 17; + + /// Creates a new instance of an `Error` from a negative error code. + #[cfg(not(target_os = "uefi"))] + #[allow(dead_code)] + pub(super) fn from_neg_error_code(code: RawOsError) -> Self { + if code < 0 { + let code = NonZeroRawOsError::new(code).expect("`code` is negative"); + Self(code) + } else { + Error::UNEXPECTED + } + } + + /// Creates a new instance of an `Error` from an UEFI error code. + #[cfg(target_os = "uefi")] + #[allow(dead_code)] + pub(super) fn from_uefi_code(code: RawOsError) -> Self { + if code & UEFI_ERROR_FLAG != 0 { + let code = NonZeroRawOsError::new(code).expect("The highest bit of `code` is set to 1"); + Self(code) + } else { + Self::UNEXPECTED + } + } + + /// Extract the raw OS error code (if this error came from the OS) + /// + /// This method is identical to [`std::io::Error::raw_os_error()`][1], except + /// that it works in `no_std` contexts. On most targets this method returns + /// `Option`, but some platforms (e.g. UEFI) may use a different primitive + /// type like `usize`. Consult with the [`RawOsError`] docs for more information. + /// + /// If this method returns `None`, the error value can still be formatted via + /// the `Display` implementation. + /// + /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error + /// [`RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html + #[inline] + pub fn raw_os_error(self) -> Option { + let code = self.0.get(); + + // note: in this method we need to cover only backends which rely on + // `Error::{from_error_code, from_errno, from_uefi_code}` methods, + // on all other backends this method always returns `None`. + + #[cfg(target_os = "uefi")] + { + if code & UEFI_ERROR_FLAG != 0 { + Some(code) + } else { + None + } + } + + #[cfg(not(target_os = "uefi"))] + { + // On most targets `std` expects positive error codes while retrieving error strings: + // - `libc`-based targets use `strerror_r` which expects positive error codes. + // - Hermit relies on the `hermit-abi` crate, which expects positive error codes: + // https://docs.rs/hermit-abi/0.4.0/src/hermit_abi/errno.rs.html#400-532 + // - WASIp1 uses the same conventions as `libc`: + // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/wasi/os.rs#L57-L67 + // + // The only exception is Solid, `std` expects negative system error codes, see: + // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/solid/error.rs#L5-L31 + if code >= 0 { + None + } else if cfg!(not(target_os = "solid_asp3")) { + code.checked_neg() + } else { + Some(code) + } + } + } + + /// Creates a new instance of an `Error` from a particular custom error code. + pub const fn new_custom(n: u16) -> Error { + // SAFETY: code > 0 as CUSTOM_START > 0 and adding `n` won't overflow `RawOsError`. + let code = Error::CUSTOM_START + (n as RawOsError); + Error(unsafe { NonZeroRawOsError::new_unchecked(code) }) + } + + /// Creates a new instance of an `Error` from a particular internal error code. + pub(crate) const fn new_internal(n: u16) -> Error { + // SAFETY: code > 0 as INTERNAL_START > 0 and adding `n` won't overflow `RawOsError`. + let code = Error::INTERNAL_START + (n as RawOsError); + Error(unsafe { NonZeroRawOsError::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, target_vendor = "win7"))] + Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure", + #[cfg(all(feature = "wasm_js", 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut dbg = f.debug_struct("Error"); + if let Some(errno) = self.raw_os_error() { + let _ = dbg.field("os_error", &errno); + #[cfg(feature = "std")] + dbg.field("description", &std::io::Error::from_raw_os_error(errno)); + } else if let Some(desc) = self.internal_desc() { + let _ = dbg.field("internal_code", &self.0.get()); + let _ = dbg.field("description", &desc); + } else { + let _ = dbg.field("unknown_code", &self.0.get()); + } + dbg.finish() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(errno) = self.raw_os_error() { + cfg_if! { + if #[cfg(feature = "std")] { + std::io::Error::from_raw_os_error(errno).fmt(f) + } else { + write!(f, "OS Error: {errno}") + } + } + } else if let Some(desc) = self.internal_desc() { + f.write_str(desc) + } else { + write!(f, "Unknown Error: {}", self.0.get()) + } + } +} diff --git a/src/rand/getrandom/util.rs b/src/rand/getrandom/util.rs new file mode 100644 index 0000000000..837508be7d --- /dev/null +++ b/src/rand/getrandom/util.rs @@ -0,0 +1,29 @@ +use core::mem::MaybeUninit; + +/// Polyfill for `maybe_uninit_slice` feature's +/// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have +/// been initialized. +#[inline(always)] +#[allow(unused_unsafe)] // TODO(MSRV 1.65): Remove this. +pub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [T] { + let ptr = ptr_from_mut::<[MaybeUninit]>(slice) as *mut [T]; + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. + unsafe { &mut *ptr } +} + +/// View an mutable initialized array as potentially-uninitialized. +/// +/// This is unsafe because it allows assigning uninitialized values into +/// `slice`, which would be undefined behavior. +#[inline(always)] +#[allow(unused_unsafe)] // TODO(MSRV 1.65): Remove this. +pub unsafe fn slice_as_uninit_mut(slice: &mut [T]) -> &mut [MaybeUninit] { + let ptr = ptr_from_mut::<[T]>(slice) as *mut [MaybeUninit]; + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. + unsafe { &mut *ptr } +} + +// TODO: MSRV(1.76.0): Replace with `core::ptr::from_mut`. +fn ptr_from_mut(r: &mut T) -> *mut T { + r +} From 2482f4cf1e79fd2fa3002cae3e030cf8898915e7 Mon Sep 17 00:00:00 2001 From: Hanyuan Li Date: Mon, 8 Sep 2025 15:25:59 +1000 Subject: [PATCH 2/6] feat: only enable vendored version if wasm32-u-u and js feature is enabled --- Cargo.lock | 73 +++++++++++++++++++++++++++++++++++------------------ Cargo.toml | 7 +++-- src/rand.rs | 11 +++++++- 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0861ad114e..a73501f165 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,13 +156,14 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "getrandom" -version = "0.2.16" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", "libc", + "r-efi", "wasi", "wasm-bindgen", ] @@ -211,9 +212,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" dependencies = [ "once_cell", "wasm-bindgen", @@ -286,6 +287,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "regex" version = "1.11.1" @@ -324,6 +331,7 @@ dependencies = [ "getrandom", "libc", "untrusted", + "wasm-bindgen", "wasm-bindgen-test", "windows-sys 0.60.2", ] @@ -344,6 +352,12 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.20" @@ -442,26 +456,31 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +version = "0.14.4+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a" +dependencies = [ + "wit-bindgen", +] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" dependencies = [ "bumpalo", "log", @@ -473,9 +492,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" dependencies = [ "cfg-if", "js-sys", @@ -486,9 +505,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -496,9 +515,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", @@ -509,18 +528,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-bindgen-test" -version = "0.3.50" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" +checksum = "80cc7f8a4114fdaa0c58383caf973fc126cf004eba25c9dc639bccd3880d55ad" dependencies = [ "js-sys", "minicov", @@ -531,9 +550,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.50" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" +checksum = "c5ada2ab788d46d4bda04c9d567702a79c8ced14f51f221646a16ed39d0e6a5d" dependencies = [ "proc-macro2", "quote", @@ -542,9 +561,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" dependencies = [ "js-sys", "wasm-bindgen", @@ -704,3 +723,9 @@ name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" diff --git a/Cargo.toml b/Cargo.toml index 278c3ccf58..df8df2f63a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -153,7 +153,7 @@ name = "ring" [dependencies] cfg-if = { version = "1.0.0", default-features = false } -getrandom = { version = "0.2.10" } +getrandom = { version = "0.3" } untrusted = { version = "0.9" } [target.'cfg(all(any(all(target_arch = "aarch64", target_endian = "little"), all(target_arch = "arm", target_endian = "little")), any(target_os = "android", target_os = "linux")))'.dependencies] @@ -165,6 +165,9 @@ libc = { version = "0.2.172", default-features = false } [target.'cfg(all(all(target_arch = "aarch64", target_endian = "little"), target_os = "windows"))'.dependencies] windows-sys = { version = "0.60", features = ["Win32_Foundation", "Win32_System_Threading"] } +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] +wasm-bindgen = { version = "0.2.101" } + [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test = { version = "0.3.37", default-features = false, features = ["std"] } @@ -186,7 +189,7 @@ std = ["alloc"] unstable-testing-arm-no-hw = [] unstable-testing-arm-no-neon = [] test_logging = [] -wasm32_unknown_unknown_js = ["getrandom/js"] +wasm32_unknown_unknown_js = ["getrandom/wasm_js"] [package.metadata.cargo-semver-checks.lints] trait_marked_deprecated = { level = "warn" } diff --git a/src/rand.rs b/src/rand.rs index 0cdefaa1c2..2aa85d9fd9 100644 --- a/src/rand.rs +++ b/src/rand.rs @@ -19,6 +19,15 @@ use crate::error; +#[cfg(all( + target_arch = "wasm32", + any( + target_os = "wasi", + all(target_os = "unknown", feature = "wasm32_unknown_unknown_js") + ) +))] +mod getrandom; + /// A secure random number generator. pub trait SecureRandom: sealed::SecureRandom { /// Fills `dest` with random bytes. @@ -165,6 +174,6 @@ impl SystemRandom { impl sealed::SecureRandom for SystemRandom { #[inline(always)] fn fill_impl(&self, dest: &mut [u8], _: crate::sealed::Arg) -> Result<(), error::Unspecified> { - getrandom::getrandom(dest).map_err(|_| error::Unspecified) + getrandom::fill(dest).map_err(|_| error::Unspecified) } } From 74d8de813c48bb415618370edbed6e985886f5f9 Mon Sep 17 00:00:00 2001 From: Hanyuan Li Date: Mon, 8 Sep 2025 15:35:41 +1000 Subject: [PATCH 3/6] feat: remove references to std feature --- src/rand/getrandom.rs | 3 --- src/rand/getrandom/error.rs | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/rand/getrandom.rs b/src/rand/getrandom.rs index de38ccba6a..796a96f3bb 100644 --- a/src/rand/getrandom.rs +++ b/src/rand/getrandom.rs @@ -58,9 +58,6 @@ mod backends; mod error; mod util; -#[cfg(feature = "std")] -mod error_std_impls; - pub use error::Error; /// Fill `dest` with random bytes from the system's preferred random number source. diff --git a/src/rand/getrandom/error.rs b/src/rand/getrandom/error.rs index 83ef78a44b..9083bd8123 100644 --- a/src/rand/getrandom/error.rs +++ b/src/rand/getrandom/error.rs @@ -1,6 +1,3 @@ -#[cfg(feature = "std")] -extern crate std; - use cfg_if::cfg_if; use core::fmt; From d5f5af0591209943b8f9c9bb4d3faffc94999af7 Mon Sep 17 00:00:00 2001 From: Hanyuan Li Date: Fri, 17 Oct 2025 16:45:28 +1100 Subject: [PATCH 4/6] fix: remove dead code from error.rs --- src/rand/getrandom/error.rs | 165 ------------------------------------ 1 file changed, 165 deletions(-) diff --git a/src/rand/getrandom/error.rs b/src/rand/getrandom/error.rs index 9083bd8123..47e70e8f10 100644 --- a/src/rand/getrandom/error.rs +++ b/src/rand/getrandom/error.rs @@ -1,4 +1,3 @@ -use cfg_if::cfg_if; use core::fmt; // This private alias mirrors `std::io::RawOsError`: @@ -34,98 +33,8 @@ cfg_if::cfg_if!( pub struct Error(NonZeroRawOsError); impl Error { - /// This target/platform is not supported by `getrandom`. - pub const UNSUPPORTED: Error = Self::new_internal(0); - /// The platform-specific `errno` returned a non-positive value. - 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); - /// Internal errors can be in the range of 2^16..2^17 const INTERNAL_START: RawOsError = 1 << 16; - /// Custom errors can be in the range of 2^17..(2^17 + 2^16) - const CUSTOM_START: RawOsError = 1 << 17; - - /// Creates a new instance of an `Error` from a negative error code. - #[cfg(not(target_os = "uefi"))] - #[allow(dead_code)] - pub(super) fn from_neg_error_code(code: RawOsError) -> Self { - if code < 0 { - let code = NonZeroRawOsError::new(code).expect("`code` is negative"); - Self(code) - } else { - Error::UNEXPECTED - } - } - - /// Creates a new instance of an `Error` from an UEFI error code. - #[cfg(target_os = "uefi")] - #[allow(dead_code)] - pub(super) fn from_uefi_code(code: RawOsError) -> Self { - if code & UEFI_ERROR_FLAG != 0 { - let code = NonZeroRawOsError::new(code).expect("The highest bit of `code` is set to 1"); - Self(code) - } else { - Self::UNEXPECTED - } - } - - /// Extract the raw OS error code (if this error came from the OS) - /// - /// This method is identical to [`std::io::Error::raw_os_error()`][1], except - /// that it works in `no_std` contexts. On most targets this method returns - /// `Option`, but some platforms (e.g. UEFI) may use a different primitive - /// type like `usize`. Consult with the [`RawOsError`] docs for more information. - /// - /// If this method returns `None`, the error value can still be formatted via - /// the `Display` implementation. - /// - /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error - /// [`RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html - #[inline] - pub fn raw_os_error(self) -> Option { - let code = self.0.get(); - - // note: in this method we need to cover only backends which rely on - // `Error::{from_error_code, from_errno, from_uefi_code}` methods, - // on all other backends this method always returns `None`. - - #[cfg(target_os = "uefi")] - { - if code & UEFI_ERROR_FLAG != 0 { - Some(code) - } else { - None - } - } - - #[cfg(not(target_os = "uefi"))] - { - // On most targets `std` expects positive error codes while retrieving error strings: - // - `libc`-based targets use `strerror_r` which expects positive error codes. - // - Hermit relies on the `hermit-abi` crate, which expects positive error codes: - // https://docs.rs/hermit-abi/0.4.0/src/hermit_abi/errno.rs.html#400-532 - // - WASIp1 uses the same conventions as `libc`: - // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/wasi/os.rs#L57-L67 - // - // The only exception is Solid, `std` expects negative system error codes, see: - // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/solid/error.rs#L5-L31 - if code >= 0 { - None - } else if cfg!(not(target_os = "solid_asp3")) { - code.checked_neg() - } else { - Some(code) - } - } - } - - /// Creates a new instance of an `Error` from a particular custom error code. - pub const fn new_custom(n: u16) -> Error { - // SAFETY: code > 0 as CUSTOM_START > 0 and adding `n` won't overflow `RawOsError`. - let code = Error::CUSTOM_START + (n as RawOsError); - Error(unsafe { NonZeroRawOsError::new_unchecked(code) }) - } /// Creates a new instance of an `Error` from a particular internal error code. pub(crate) const fn new_internal(n: u16) -> Error { @@ -133,78 +42,4 @@ impl Error { let code = Error::INTERNAL_START + (n as RawOsError); Error(unsafe { NonZeroRawOsError::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, target_vendor = "win7"))] - Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure", - #[cfg(all(feature = "wasm_js", 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 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut dbg = f.debug_struct("Error"); - if let Some(errno) = self.raw_os_error() { - let _ = dbg.field("os_error", &errno); - #[cfg(feature = "std")] - dbg.field("description", &std::io::Error::from_raw_os_error(errno)); - } else if let Some(desc) = self.internal_desc() { - let _ = dbg.field("internal_code", &self.0.get()); - let _ = dbg.field("description", &desc); - } else { - let _ = dbg.field("unknown_code", &self.0.get()); - } - dbg.finish() - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(errno) = self.raw_os_error() { - cfg_if! { - if #[cfg(feature = "std")] { - std::io::Error::from_raw_os_error(errno).fmt(f) - } else { - write!(f, "OS Error: {errno}") - } - } - } else if let Some(desc) = self.internal_desc() { - f.write_str(desc) - } else { - write!(f, "Unknown Error: {}", self.0.get()) - } - } } From bdec6dd0cfa3c1db0f2d4711ebfbc59e0ae003aa Mon Sep 17 00:00:00 2001 From: Hanyuan Li Date: Fri, 17 Oct 2025 16:46:06 +1100 Subject: [PATCH 5/6] feat: set upstream to be getrandom v0.3.3 --- src/rand/getrandom.rs | 10 ++++++++-- src/rand/getrandom/backends/wasm_js.rs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rand/getrandom.rs b/src/rand/getrandom.rs index 796a96f3bb..b2f8fcec0a 100644 --- a/src/rand/getrandom.rs +++ b/src/rand/getrandom.rs @@ -1,6 +1,6 @@ // This file, and all code in `rand/getrandom`, are vendored from // the `getrandom` project: -// https://github.com/rust-random/getrandom/tree/78ef61a0e7a5dc876da0b496998464630a4c0bf0 +// https://github.com/rust-random/getrandom/blob/v0.3.3 // // Copyright (c) 2018-2025 The rust-random Project Developers // Copyright (c) 2014 The Rust Project Developers @@ -125,5 +125,11 @@ pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<&mut [u8], Error> { // SAFETY: `dest` has been fully initialized by `imp::fill_inner` // since it returned `Ok`. - Ok(unsafe { util::slice_assume_init_mut(dest) }) + Ok(unsafe { + #[cfg(getrandom_msan)] + __msan_unpoison(dest.as_mut_ptr().cast(), dest.len()); + + util::slice_assume_init_mut(dest) + }) } + diff --git a/src/rand/getrandom/backends/wasm_js.rs b/src/rand/getrandom/backends/wasm_js.rs index 8d99b83d61..9c599b6b2a 100644 --- a/src/rand/getrandom/backends/wasm_js.rs +++ b/src/rand/getrandom/backends/wasm_js.rs @@ -67,4 +67,4 @@ extern "C" { impl Error { /// The environment does not support the Web Crypto API. pub(crate) const WEB_CRYPTO: Error = Self::new_internal(10); -} \ No newline at end of file +} From b1b2aab3c10ffede89da67527d35b7b0151db129 Mon Sep 17 00:00:00 2001 From: Hanyuan Li Date: Fri, 17 Oct 2025 16:54:41 +1100 Subject: [PATCH 6/6] feat: rename getrandom.rs for easier upstream comparisons --- src/rand.rs | 1 + src/rand/{getrandom.rs => getrandom/lib.rs} | 0 2 files changed, 1 insertion(+) rename src/rand/{getrandom.rs => getrandom/lib.rs} (100%) diff --git a/src/rand.rs b/src/rand.rs index 2aa85d9fd9..9b076568b0 100644 --- a/src/rand.rs +++ b/src/rand.rs @@ -26,6 +26,7 @@ use crate::error; all(target_os = "unknown", feature = "wasm32_unknown_unknown_js") ) ))] +#[path = "rand/getrandom/lib.rs"] mod getrandom; /// A secure random number generator. diff --git a/src/rand/getrandom.rs b/src/rand/getrandom/lib.rs similarity index 100% rename from src/rand/getrandom.rs rename to src/rand/getrandom/lib.rs