diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
| commit | 8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch) | |
| tree | 22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/getrandom/src/backends | |
| parent | 4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff) | |
chore: add vendor directory
Diffstat (limited to 'vendor/getrandom/src/backends')
22 files changed, 1557 insertions, 0 deletions
diff --git a/vendor/getrandom/src/backends/apple_other.rs b/vendor/getrandom/src/backends/apple_other.rs new file mode 100644 index 00000000..c7b51c0e --- /dev/null +++ b/vendor/getrandom/src/backends/apple_other.rs @@ -0,0 +1,21 @@ +//! Implementation for iOS, tvOS, and watchOS where `getentropy` is unavailable. +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + let dst_ptr = dest.as_mut_ptr().cast::<c_void>(); + let ret = unsafe { libc::CCRandomGenerateBytes(dst_ptr, dest.len()) }; + if ret == libc::kCCSuccess { + Ok(()) + } else { + Err(Error::IOS_RANDOM_GEN) + } +} + +impl Error { + /// Call to `CCRandomGenerateBytes` failed. + pub(crate) const IOS_RANDOM_GEN: Error = Self::new_internal(10); +} diff --git a/vendor/getrandom/src/backends/custom.rs b/vendor/getrandom/src/backends/custom.rs new file mode 100644 index 00000000..c505481a --- /dev/null +++ b/vendor/getrandom/src/backends/custom.rs @@ -0,0 +1,13 @@ +//! An implementation which calls out to an externally defined function. +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + extern "Rust" { + fn __getrandom_v03_custom(dest: *mut u8, len: usize) -> Result<(), Error>; + } + unsafe { __getrandom_v03_custom(dest.as_mut_ptr().cast(), dest.len()) } +} diff --git a/vendor/getrandom/src/backends/efi_rng.rs b/vendor/getrandom/src/backends/efi_rng.rs new file mode 100644 index 00000000..768c8cc8 --- /dev/null +++ b/vendor/getrandom/src/backends/efi_rng.rs @@ -0,0 +1,124 @@ +//! Implementation for UEFI using EFI_RNG_PROTOCOL +use crate::Error; +use core::{ + mem::MaybeUninit, + ptr::{self, null_mut, NonNull}, + sync::atomic::{AtomicPtr, Ordering::Relaxed}, +}; +use r_efi::{ + efi::{BootServices, Handle}, + protocols::rng, +}; + +extern crate std; + +pub use crate::util::{inner_u32, inner_u64}; + +#[cfg(not(target_os = "uefi"))] +compile_error!("`efi_rng` backend can be enabled only for UEFI targets!"); + +static RNG_PROTOCOL: AtomicPtr<rng::Protocol> = AtomicPtr::new(null_mut()); + +#[cold] +#[inline(never)] +fn init() -> Result<NonNull<rng::Protocol>, Error> { + const HANDLE_SIZE: usize = size_of::<Handle>(); + + let boot_services = std::os::uefi::env::boot_services() + .ok_or(Error::BOOT_SERVICES_UNAVAILABLE)? + .cast::<BootServices>(); + + let mut handles = [ptr::null_mut(); 16]; + // `locate_handle` operates with length in bytes + let mut buf_size = handles.len() * HANDLE_SIZE; + let mut guid = rng::PROTOCOL_GUID; + let ret = unsafe { + ((*boot_services.as_ptr()).locate_handle)( + r_efi::efi::BY_PROTOCOL, + &mut guid, + null_mut(), + &mut buf_size, + handles.as_mut_ptr(), + ) + }; + + if ret.is_error() { + return Err(Error::from_uefi_code(ret.as_usize())); + } + + let handles_len = buf_size / HANDLE_SIZE; + let handles = handles.get(..handles_len).ok_or(Error::UNEXPECTED)?; + + let system_handle = std::os::uefi::env::image_handle(); + for &handle in handles { + let mut protocol: MaybeUninit<*mut rng::Protocol> = MaybeUninit::uninit(); + + let mut protocol_guid = rng::PROTOCOL_GUID; + let ret = unsafe { + ((*boot_services.as_ptr()).open_protocol)( + handle, + &mut protocol_guid, + protocol.as_mut_ptr().cast(), + system_handle.as_ptr(), + ptr::null_mut(), + r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, + ) + }; + + let protocol = if ret.is_error() { + continue; + } else { + let protocol = unsafe { protocol.assume_init() }; + NonNull::new(protocol).ok_or(Error::UNEXPECTED)? + }; + + // Try to use the acquired protocol handle + let mut buf = [0u8; 8]; + let mut alg_guid = rng::ALGORITHM_RAW; + let ret = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + &mut alg_guid, + buf.len(), + buf.as_mut_ptr(), + ) + }; + + if ret.is_error() { + continue; + } + + RNG_PROTOCOL.store(protocol.as_ptr(), Relaxed); + return Ok(protocol); + } + Err(Error::NO_RNG_HANDLE) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + let protocol = match NonNull::new(RNG_PROTOCOL.load(Relaxed)) { + Some(p) => p, + None => init()?, + }; + + let mut alg_guid = rng::ALGORITHM_RAW; + let ret = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + &mut alg_guid, + dest.len(), + dest.as_mut_ptr().cast::<u8>(), + ) + }; + + if ret.is_error() { + Err(Error::from_uefi_code(ret.as_usize())) + } else { + Ok(()) + } +} + +impl Error { + pub(crate) const BOOT_SERVICES_UNAVAILABLE: Error = Self::new_internal(10); + pub(crate) const NO_RNG_HANDLE: Error = Self::new_internal(11); +} diff --git a/vendor/getrandom/src/backends/esp_idf.rs b/vendor/getrandom/src/backends/esp_idf.rs new file mode 100644 index 00000000..4d1689dc --- /dev/null +++ b/vendor/getrandom/src/backends/esp_idf.rs @@ -0,0 +1,21 @@ +//! Implementation for ESP-IDF +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +extern "C" { + fn esp_fill_random(buf: *mut c_void, len: usize) -> u32; +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + // Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`) + // will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process: + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html + // + // However tracking if some of these entropy sources is enabled is way too difficult to implement here + unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) }; + + Ok(()) +} diff --git a/vendor/getrandom/src/backends/fuchsia.rs b/vendor/getrandom/src/backends/fuchsia.rs new file mode 100644 index 00000000..b5f1ade5 --- /dev/null +++ b/vendor/getrandom/src/backends/fuchsia.rs @@ -0,0 +1,16 @@ +//! Implementation for Fuchsia Zircon +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +#[link(name = "zircon")] +extern "C" { + fn zx_cprng_draw(buffer: *mut u8, length: usize); +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + unsafe { zx_cprng_draw(dest.as_mut_ptr().cast::<u8>(), dest.len()) } + Ok(()) +} diff --git a/vendor/getrandom/src/backends/getentropy.rs b/vendor/getrandom/src/backends/getentropy.rs new file mode 100644 index 00000000..ed181f01 --- /dev/null +++ b/vendor/getrandom/src/backends/getentropy.rs @@ -0,0 +1,27 @@ +//! Implementation using getentropy(2) +//! +//! Available since: +//! - macOS 10.12 +//! - OpenBSD 5.6 +//! - Emscripten 2.0.5 +//! - vita newlib since Dec 2021 +//! +//! For these targets, we use getentropy(2) because getrandom(2) doesn't exist. +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +mod util_libc; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + for chunk in dest.chunks_mut(256) { + let ret = unsafe { libc::getentropy(chunk.as_mut_ptr().cast::<c_void>(), chunk.len()) }; + if ret != 0 { + return Err(util_libc::last_os_error()); + } + } + Ok(()) +} diff --git a/vendor/getrandom/src/backends/getrandom.rs b/vendor/getrandom/src/backends/getrandom.rs new file mode 100644 index 00000000..27d5a1f5 --- /dev/null +++ b/vendor/getrandom/src/backends/getrandom.rs @@ -0,0 +1,31 @@ +//! Implementation using getrandom(2). +//! +//! Available since: +//! - Linux Kernel 3.17, Glibc 2.25, Musl 1.1.20 +//! - Android API level 23 (Marshmallow) +//! - NetBSD 10.0 +//! - FreeBSD 12.0 +//! - illumos since Dec 2018 +//! - DragonFly 5.7 +//! - Hurd Glibc 2.31 +//! - shim-3ds since Feb 2022 +//! +//! For these platforms, we always use the default pool and never set the +//! GRND_RANDOM flag to use the /dev/random pool. On Linux/Android/Hurd, using +//! GRND_RANDOM is not recommended. On NetBSD/FreeBSD/Dragonfly/3ds, it does +//! nothing. On illumos, the default pool is used to implement getentropy(2), +//! so we assume it is acceptable here. +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +mod util_libc; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + util_libc::sys_fill_exact(dest, |buf| unsafe { + libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) + }) +} diff --git a/vendor/getrandom/src/backends/hermit.rs b/vendor/getrandom/src/backends/hermit.rs new file mode 100644 index 00000000..34d7cdbb --- /dev/null +++ b/vendor/getrandom/src/backends/hermit.rs @@ -0,0 +1,53 @@ +//! Implementation for Hermit +use crate::Error; +use core::mem::MaybeUninit; + +extern "C" { + fn sys_read_entropy(buffer: *mut u8, length: usize, flags: u32) -> isize; + // Note that `sys_secure_rand32/64` are implemented using `sys_read_entropy`: + // https://github.com/hermit-os/kernel/blob/430da84/src/syscalls/entropy.rs#L62-L104 + // But this may change in future and can depend on compilation target, + // so to future-proof we use these "syscalls". + fn sys_secure_rand32(value: *mut u32) -> i32; + fn sys_secure_rand64(value: *mut u64) -> i32; +} + +#[inline] +pub fn inner_u32() -> Result<u32, Error> { + let mut res = MaybeUninit::uninit(); + let ret = unsafe { sys_secure_rand32(res.as_mut_ptr()) }; + match ret { + 0 => Ok(unsafe { res.assume_init() }), + -1 => Err(Error::UNSUPPORTED), + _ => Err(Error::UNEXPECTED), + } +} + +#[inline] +pub fn inner_u64() -> Result<u64, Error> { + let mut res = MaybeUninit::uninit(); + let ret = unsafe { sys_secure_rand64(res.as_mut_ptr()) }; + match ret { + 0 => Ok(unsafe { res.assume_init() }), + -1 => Err(Error::UNSUPPORTED), + _ => Err(Error::UNEXPECTED), + } +} + +#[inline] +pub fn fill_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + while !dest.is_empty() { + let res = unsafe { sys_read_entropy(dest.as_mut_ptr().cast::<u8>(), dest.len(), 0) }; + match res { + res if res > 0 => { + let len = usize::try_from(res).map_err(|_| Error::UNEXPECTED)?; + dest = dest.get_mut(len..).ok_or(Error::UNEXPECTED)?; + } + code => { + let code = i32::try_from(code).map_err(|_| Error::UNEXPECTED)?; + return Err(Error::from_neg_error_code(code)); + } + } + } + Ok(()) +} diff --git a/vendor/getrandom/src/backends/linux_android_with_fallback.rs b/vendor/getrandom/src/backends/linux_android_with_fallback.rs new file mode 100644 index 00000000..2ad8f0a4 --- /dev/null +++ b/vendor/getrandom/src/backends/linux_android_with_fallback.rs @@ -0,0 +1,101 @@ +//! Implementation for Linux / Android with `/dev/urandom` fallback +use super::use_file; +use crate::Error; +use core::{ + ffi::c_void, + mem::{transmute, MaybeUninit}, + ptr::NonNull, + sync::atomic::{AtomicPtr, Ordering}, +}; +use use_file::util_libc; + +pub use crate::util::{inner_u32, inner_u64}; + +type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t; + +/// Sentinel value which indicates that `libc::getrandom` either not available, +/// or not supported by kernel. +const NOT_AVAILABLE: NonNull<c_void> = unsafe { NonNull::new_unchecked(usize::MAX as *mut c_void) }; + +static GETRANDOM_FN: AtomicPtr<c_void> = AtomicPtr::new(core::ptr::null_mut()); + +#[cold] +#[inline(never)] +fn init() -> NonNull<c_void> { + // Use static linking to `libc::getrandom` on MUSL targets and `dlsym` everywhere else + #[cfg(not(target_env = "musl"))] + let raw_ptr = { + static NAME: &[u8] = b"getrandom\0"; + let name_ptr = NAME.as_ptr().cast::<libc::c_char>(); + unsafe { libc::dlsym(libc::RTLD_DEFAULT, name_ptr) } + }; + #[cfg(target_env = "musl")] + let raw_ptr = { + let fptr: GetRandomFn = libc::getrandom; + unsafe { transmute::<GetRandomFn, *mut c_void>(fptr) } + }; + + let res_ptr = match NonNull::new(raw_ptr) { + Some(fptr) => { + let getrandom_fn = unsafe { transmute::<NonNull<c_void>, GetRandomFn>(fptr) }; + let dangling_ptr = NonNull::dangling().as_ptr(); + // Check that `getrandom` syscall is supported by kernel + let res = unsafe { getrandom_fn(dangling_ptr, 0, 0) }; + if cfg!(getrandom_test_linux_fallback) { + NOT_AVAILABLE + } else if res.is_negative() { + match util_libc::last_os_error().raw_os_error() { + Some(libc::ENOSYS) => NOT_AVAILABLE, // No kernel support + // The fallback on EPERM is intentionally not done on Android since this workaround + // seems to be needed only for specific Linux-based products that aren't based + // on Android. See https://github.com/rust-random/getrandom/issues/229. + #[cfg(target_os = "linux")] + Some(libc::EPERM) => NOT_AVAILABLE, // Blocked by seccomp + _ => fptr, + } + } else { + fptr + } + } + None => NOT_AVAILABLE, + }; + + #[cfg(getrandom_test_linux_without_fallback)] + if res_ptr == NOT_AVAILABLE { + panic!("Fallback is triggered with enabled `getrandom_test_linux_without_fallback`") + } + + GETRANDOM_FN.store(res_ptr.as_ptr(), Ordering::Release); + res_ptr +} + +// Prevent inlining of the fallback implementation +#[inline(never)] +fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + use_file::fill_inner(dest) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + // Despite being only a single atomic variable, we still cannot always use + // Ordering::Relaxed, as we need to make sure a successful call to `init` + // is "ordered before" any data read through the returned pointer (which + // occurs when the function is called). Our implementation mirrors that of + // the one in libstd, meaning that the use of non-Relaxed operations is + // probably unnecessary. + let raw_ptr = GETRANDOM_FN.load(Ordering::Acquire); + let fptr = match NonNull::new(raw_ptr) { + Some(p) => p, + None => init(), + }; + + if fptr == NOT_AVAILABLE { + use_file_fallback(dest) + } else { + // note: `transmute` is currently the only way to convert a pointer into a function reference + let getrandom_fn = unsafe { transmute::<NonNull<c_void>, GetRandomFn>(fptr) }; + util_libc::sys_fill_exact(dest, |buf| unsafe { + getrandom_fn(buf.as_mut_ptr().cast(), buf.len(), 0) + }) + } +} diff --git a/vendor/getrandom/src/backends/linux_raw.rs b/vendor/getrandom/src/backends/linux_raw.rs new file mode 100644 index 00000000..4a59eef0 --- /dev/null +++ b/vendor/getrandom/src/backends/linux_raw.rs @@ -0,0 +1,136 @@ +//! Implementation for Linux / Android using `asm!`-based syscalls. +use crate::{Error, MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +compile_error!("`linux_raw` backend can be enabled only for Linux/Android targets!"); + +#[allow(non_upper_case_globals)] +unsafe fn getrandom_syscall(buf: *mut u8, buflen: usize, flags: u32) -> isize { + let r0; + + // Based on `rustix` and `linux-raw-sys` code. + cfg_if! { + if #[cfg(target_arch = "arm")] { + const __NR_getrandom: u32 = 384; + // In thumb-mode, r7 is the frame pointer and is not permitted to be used in + // an inline asm operand, so we have to use a different register and copy it + // into r7 inside the inline asm. + // Theoretically, we could detect thumb mode in the build script, but several + // register moves are cheap enough compared to the syscall cost, so we do not + // bother with it. + core::arch::asm!( + "mov {tmp}, r7", + "mov r7, {nr}", + "svc 0", + "mov r7, {tmp}", + nr = const __NR_getrandom, + tmp = out(reg) _, + inlateout("r0") buf => r0, + in("r1") buflen, + in("r2") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "aarch64")] { + const __NR_getrandom: u32 = 278; + core::arch::asm!( + "svc 0", + in("x8") __NR_getrandom, + inlateout("x0") buf => r0, + in("x1") buflen, + in("x2") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "loongarch64")] { + const __NR_getrandom: u32 = 278; + core::arch::asm!( + "syscall 0", + in("$a7") __NR_getrandom, + inlateout("$a0") buf => r0, + in("$a1") buflen, + in("$a2") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { + const __NR_getrandom: u32 = 278; + core::arch::asm!( + "ecall", + in("a7") __NR_getrandom, + inlateout("a0") buf => r0, + in("a1") buflen, + in("a2") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "s390x")] { + const __NR_getrandom: u32 = 349; + core::arch::asm!( + "svc 0", + in("r1") __NR_getrandom, + inlateout("r2") buf => r0, + in("r3") buflen, + in("r4") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "x86")] { + const __NR_getrandom: u32 = 355; + // `int 0x80` is famously slow, but implementing vDSO is too complex + // and `sysenter`/`syscall` have their own portability issues, + // so we use the simple "legacy" way of doing syscalls. + core::arch::asm!( + "int $$0x80", + in("eax") __NR_getrandom, + in("ebx") buf, + in("ecx") buflen, + in("edx") flags, + lateout("eax") r0, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "x86_64")] { + #[cfg(target_pointer_width = "64")] + const __NR_getrandom: u32 = 318; + #[cfg(target_pointer_width = "32")] + const __NR_getrandom: u32 = (1 << 30) + 318; + + core::arch::asm!( + "syscall", + in("rax") __NR_getrandom, + in("rdi") buf, + in("rsi") buflen, + in("rdx") flags, + lateout("rax") r0, + lateout("rcx") _, + lateout("r11") _, + options(nostack, preserves_flags) + ); + } else { + compile_error!("`linux_raw` backend does not support this target arch"); + } + } + + r0 +} + +#[inline] +pub fn fill_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + // Value of this error code is stable across all target arches. + const EINTR: isize = -4; + + loop { + let ret = unsafe { getrandom_syscall(dest.as_mut_ptr().cast(), dest.len(), 0) }; + match usize::try_from(ret) { + Ok(0) => return Err(Error::UNEXPECTED), + Ok(len) => { + dest = dest.get_mut(len..).ok_or(Error::UNEXPECTED)?; + if dest.is_empty() { + return Ok(()); + } + } + Err(_) if ret == EINTR => continue, + Err(_) => { + let code = i32::try_from(ret).map_err(|_| Error::UNEXPECTED)?; + return Err(Error::from_neg_error_code(code)); + } + } + } +} diff --git a/vendor/getrandom/src/backends/netbsd.rs b/vendor/getrandom/src/backends/netbsd.rs new file mode 100644 index 00000000..f228a8b1 --- /dev/null +++ b/vendor/getrandom/src/backends/netbsd.rs @@ -0,0 +1,78 @@ +//! Implementation for NetBSD +//! +//! `getrandom(2)` was introduced in NetBSD 10. To support older versions we +//! implement our own weak linkage to it, and provide a fallback based on the +//! KERN_ARND sysctl. +use crate::Error; +use core::{ + cmp, + ffi::c_void, + mem::{self, MaybeUninit}, + ptr, + sync::atomic::{AtomicPtr, Ordering}, +}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +mod util_libc; + +unsafe extern "C" fn polyfill_using_kern_arand( + buf: *mut c_void, + buflen: libc::size_t, + flags: libc::c_uint, +) -> libc::ssize_t { + debug_assert_eq!(flags, 0); + + const MIB_LEN: libc::c_uint = 2; + static MIB: [libc::c_int; MIB_LEN as usize] = [libc::CTL_KERN, libc::KERN_ARND]; + + // NetBSD will only return up to 256 bytes at a time, and + // older NetBSD kernels will fail on longer buffers. + let mut len = cmp::min(buflen, 256); + let ret = unsafe { libc::sysctl(MIB.as_ptr(), MIB_LEN, buf, &mut len, ptr::null(), 0) }; + + match ret { + 0 if len <= 256 => libc::ssize_t::try_from(len).expect("len is in the range of 0..=256"), + -1 => -1, + // Zero return result will be converted into `Error::UNEXPECTED` by `sys_fill_exact` + _ => 0, + } +} + +type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t; + +static GETRANDOM: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); + +#[cold] +#[inline(never)] +fn init() -> *mut c_void { + static NAME: &[u8] = b"getrandom\0"; + let name_ptr = NAME.as_ptr().cast::<libc::c_char>(); + let mut ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name_ptr) }; + if ptr.is_null() || cfg!(getrandom_test_netbsd_fallback) { + // Verify `polyfill_using_kern_arand` has the right signature. + const POLYFILL: GetRandomFn = polyfill_using_kern_arand; + ptr = POLYFILL as *mut c_void; + } + GETRANDOM.store(ptr, Ordering::Release); + ptr +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + // Despite being only a single atomic variable, we still cannot always use + // Ordering::Relaxed, as we need to make sure a successful call to `init` + // is "ordered before" any data read through the returned pointer (which + // occurs when the function is called). Our implementation mirrors that of + // the one in libstd, meaning that the use of non-Relaxed operations is + // probably unnecessary. + let mut fptr = GETRANDOM.load(Ordering::Acquire); + if fptr.is_null() { + fptr = init(); + } + let fptr = unsafe { mem::transmute::<*mut c_void, GetRandomFn>(fptr) }; + util_libc::sys_fill_exact(dest, |buf| unsafe { + fptr(buf.as_mut_ptr().cast::<c_void>(), buf.len(), 0) + }) +} diff --git a/vendor/getrandom/src/backends/rdrand.rs b/vendor/getrandom/src/backends/rdrand.rs new file mode 100644 index 00000000..609fcc38 --- /dev/null +++ b/vendor/getrandom/src/backends/rdrand.rs @@ -0,0 +1,182 @@ +//! RDRAND backend for x86(-64) targets +use crate::{util::slice_as_uninit, Error}; +use core::mem::{size_of, MaybeUninit}; + +#[path = "../lazy.rs"] +mod lazy; + +#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))] +compile_error!("`rdrand` backend can be enabled only for x86 and x86-64 targets!"); + +cfg_if! { + if #[cfg(target_arch = "x86_64")] { + use core::arch::x86_64 as arch; + use arch::_rdrand64_step as rdrand_step; + type Word = u64; + } else if #[cfg(target_arch = "x86")] { + use core::arch::x86 as arch; + use arch::_rdrand32_step as rdrand_step; + type Word = u32; + } +} + +static RDRAND_GOOD: lazy::LazyBool = lazy::LazyBool::new(); + +// Recommendation from "Intel® Digital Random Number Generator (DRNG) Software +// Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures +// Software Developer’s Manual" - Volume 1 - Section 7.3.17.1. +const RETRY_LIMIT: usize = 10; + +#[target_feature(enable = "rdrand")] +unsafe fn rdrand() -> Option<Word> { + for _ in 0..RETRY_LIMIT { + let mut val = 0; + if rdrand_step(&mut val) == 1 { + return Some(val); + } + } + None +} + +// "rdrand" target feature requires "+rdrand" flag, see https://github.com/rust-lang/rust/issues/49653. +#[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))] +compile_error!( + "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrand." +); + +// Run a small self-test to make sure we aren't repeating values +// Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c +// Fails with probability < 2^(-90) on 32-bit systems +#[target_feature(enable = "rdrand")] +unsafe fn self_test() -> bool { + // On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision. + let mut prev = !0; // TODO(MSRV 1.43): Move to usize::MAX + let mut fails = 0; + for _ in 0..8 { + match rdrand() { + Some(val) if val == prev => fails += 1, + Some(val) => prev = val, + None => return false, + }; + } + fails <= 2 +} + +fn is_rdrand_good() -> bool { + #[cfg(not(target_feature = "rdrand"))] + { + // SAFETY: All Rust x86 targets are new enough to have CPUID, and we + // check that leaf 1 is supported before using it. + let cpuid0 = unsafe { arch::__cpuid(0) }; + if cpuid0.eax < 1 { + return false; + } + let cpuid1 = unsafe { arch::__cpuid(1) }; + + let vendor_id = [ + cpuid0.ebx.to_le_bytes(), + cpuid0.edx.to_le_bytes(), + cpuid0.ecx.to_le_bytes(), + ]; + if vendor_id == [*b"Auth", *b"enti", *b"cAMD"] { + let mut family = (cpuid1.eax >> 8) & 0xF; + if family == 0xF { + family += (cpuid1.eax >> 20) & 0xFF; + } + // AMD CPUs families before 17h (Zen) sometimes fail to set CF when + // RDRAND fails after suspend. Don't use RDRAND on those families. + // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286 + if family < 0x17 { + return false; + } + } + + const RDRAND_FLAG: u32 = 1 << 30; + if cpuid1.ecx & RDRAND_FLAG == 0 { + return false; + } + } + + // SAFETY: We have already checked that rdrand is available. + unsafe { self_test() } +} + +// TODO: make this function safe when we have feature(target_feature_11) +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_exact(dest: &mut [MaybeUninit<u8>]) -> Option<()> { + // We use chunks_exact_mut instead of chunks_mut as it allows almost all + // calls to memcpy to be elided by the compiler. + let mut chunks = dest.chunks_exact_mut(size_of::<Word>()); + for chunk in chunks.by_ref() { + let src = rdrand()?.to_ne_bytes(); + chunk.copy_from_slice(slice_as_uninit(&src)); + } + + let tail = chunks.into_remainder(); + let n = tail.len(); + if n > 0 { + let src = rdrand()?.to_ne_bytes(); + tail.copy_from_slice(slice_as_uninit(&src[..n])); + } + Some(()) +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_u32() -> Option<u32> { + rdrand().map(crate::util::truncate) +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_u64() -> Option<u64> { + rdrand() +} + +#[cfg(target_arch = "x86")] +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_u32() -> Option<u32> { + rdrand() +} + +#[cfg(target_arch = "x86")] +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_u64() -> Option<u64> { + let a = rdrand()?; + let b = rdrand()?; + Some((u64::from(a) << 32) | u64::from(b)) +} + +#[inline] +pub fn inner_u32() -> Result<u32, Error> { + if !RDRAND_GOOD.unsync_init(is_rdrand_good) { + return Err(Error::NO_RDRAND); + } + // SAFETY: After this point, we know rdrand is supported. + unsafe { rdrand_u32() }.ok_or(Error::FAILED_RDRAND) +} + +#[inline] +pub fn inner_u64() -> Result<u64, Error> { + if !RDRAND_GOOD.unsync_init(is_rdrand_good) { + return Err(Error::NO_RDRAND); + } + // SAFETY: After this point, we know rdrand is supported. + unsafe { rdrand_u64() }.ok_or(Error::FAILED_RDRAND) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + if !RDRAND_GOOD.unsync_init(is_rdrand_good) { + return Err(Error::NO_RDRAND); + } + // 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); +} diff --git a/vendor/getrandom/src/backends/rndr.rs b/vendor/getrandom/src/backends/rndr.rs new file mode 100644 index 00000000..eea741a2 --- /dev/null +++ b/vendor/getrandom/src/backends/rndr.rs @@ -0,0 +1,145 @@ +//! RNDR register backend for aarch64 targets +//! +//! Arm Architecture Reference Manual for A-profile architecture: +//! ARM DDI 0487K.a, ID032224, D23.2.147 RNDR, Random Number +use crate::{ + util::{slice_as_uninit, truncate}, + Error, +}; +use core::arch::asm; +use core::mem::{size_of, MaybeUninit}; + +#[cfg(not(target_arch = "aarch64"))] +compile_error!("the `rndr` backend can be enabled only for AArch64 targets!"); + +const RETRY_LIMIT: usize = 5; + +/// Read a random number from the aarch64 RNDR register +/// +/// Callers must ensure that FEAT_RNG is available on the system +/// The function assumes that the RNDR register is available +/// If it fails to read a random number, it will retry up to 5 times +/// After 5 failed reads the function will return `None` +#[target_feature(enable = "rand")] +unsafe fn rndr() -> Option<u64> { + for _ in 0..RETRY_LIMIT { + let mut x: u64; + let mut nzcv: u64; + + // AArch64 RNDR register is accessible by s3_3_c2_c4_0 + asm!( + "mrs {x}, RNDR", + "mrs {nzcv}, NZCV", + x = out(reg) x, + nzcv = out(reg) nzcv, + ); + + // If the hardware returns a genuine random number, PSTATE.NZCV is set to 0b0000 + if nzcv == 0 { + return Some(x); + } + } + + None +} + +#[target_feature(enable = "rand")] +unsafe fn rndr_fill(dest: &mut [MaybeUninit<u8>]) -> Option<()> { + let mut chunks = dest.chunks_exact_mut(size_of::<u64>()); + for chunk in chunks.by_ref() { + let src = rndr()?.to_ne_bytes(); + chunk.copy_from_slice(slice_as_uninit(&src)); + } + + let tail = chunks.into_remainder(); + let n = tail.len(); + if n > 0 { + let src = rndr()?.to_ne_bytes(); + tail.copy_from_slice(slice_as_uninit(&src[..n])); + } + Some(()) +} + +#[cfg(target_feature = "rand")] +fn is_rndr_available() -> bool { + true +} + +#[cfg(not(target_feature = "rand"))] +fn is_rndr_available() -> bool { + #[path = "../lazy.rs"] + mod lazy; + static RNDR_GOOD: lazy::LazyBool = lazy::LazyBool::new(); + + cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + extern crate std; + RNDR_GOOD.unsync_init(|| std::arch::is_aarch64_feature_detected!("rand")) + } else if #[cfg(target_os = "linux")] { + /// Check whether FEAT_RNG is available on the system + /// + /// Requires the caller either be running in EL1 or be on a system supporting MRS + /// emulation. Due to the above, the implementation is currently restricted to Linux. + /// + /// Relying on runtime detection bumps minimum supported Linux kernel version to 4.11. + fn mrs_check() -> bool { + let mut id_aa64isar0: u64; + + // If FEAT_RNG is implemented, ID_AA64ISAR0_EL1.RNDR (bits 60-63) are 0b0001 + // This is okay to do from EL0 in Linux because Linux will emulate MRS as per + // https://docs.kernel.org/arch/arm64/cpu-feature-registers.html + unsafe { + asm!( + "mrs {id}, ID_AA64ISAR0_EL1", + id = out(reg) id_aa64isar0, + ); + } + + (id_aa64isar0 >> 60) & 0xf >= 1 + } + + RNDR_GOOD.unsync_init(mrs_check) + } else { + compile_error!( + "RNDR `no_std` runtime detection is currently supported only on Linux targets. \ + Either enable the `std` crate feature, or `rand` target feature at compile time." + ); + } + } +} + +#[inline] +pub fn inner_u32() -> Result<u32, Error> { + if !is_rndr_available() { + return Err(Error::RNDR_NOT_AVAILABLE); + } + // SAFETY: after this point, we know the `rand` target feature is enabled + let res = unsafe { rndr() }; + res.map(truncate).ok_or(Error::RNDR_FAILURE) +} + +#[inline] +pub fn inner_u64() -> Result<u64, Error> { + if !is_rndr_available() { + return Err(Error::RNDR_NOT_AVAILABLE); + } + // SAFETY: after this point, we know the `rand` target feature is enabled + let res = unsafe { rndr() }; + res.ok_or(Error::RNDR_FAILURE) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + if !is_rndr_available() { + return Err(Error::RNDR_NOT_AVAILABLE); + } + // SAFETY: after this point, we know the `rand` target feature is enabled + unsafe { rndr_fill(dest).ok_or(Error::RNDR_FAILURE) } +} + +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); +} diff --git a/vendor/getrandom/src/backends/solaris.rs b/vendor/getrandom/src/backends/solaris.rs new file mode 100644 index 00000000..c27f91a5 --- /dev/null +++ b/vendor/getrandom/src/backends/solaris.rs @@ -0,0 +1,42 @@ +//! Solaris implementation using getrandom(2). +//! +//! While getrandom(2) has been available since Solaris 11.3, it has a few +//! quirks not present on other OSes. First, on Solaris 11.3, calls will always +//! fail if bufsz > 1024. Second, it will always either fail or completely fill +//! the buffer (returning bufsz). Third, error is indicated by returning 0, +//! rather than by returning -1. Finally, "if GRND_RANDOM is not specified +//! then getrandom(2) is always a non blocking call". This _might_ imply that +//! in early-boot scenarios with low entropy, getrandom(2) will not properly +//! block. To be safe, we set GRND_RANDOM, mirroring the man page examples. +//! +//! For more information, see the man page linked in lib.rs and this blog post: +//! https://blogs.oracle.com/solaris/post/solaris-new-system-calls-getentropy2-and-getrandom2 +//! which also explains why this crate should not use getentropy(2). +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +mod util_libc; + +const MAX_BYTES: usize = 1024; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + for chunk in dest.chunks_mut(MAX_BYTES) { + let ptr = chunk.as_mut_ptr().cast::<c_void>(); + let ret = unsafe { libc::getrandom(ptr, chunk.len(), libc::GRND_RANDOM) }; + // In case the man page has a typo, we also check for negative ret. + // If getrandom(2) succeeds, it should have completely filled chunk. + match usize::try_from(ret) { + // Good. Keep going. + Ok(ret) if ret == chunk.len() => {} + // The syscall failed. + Ok(0) => return Err(util_libc::last_os_error()), + // All other cases should be impossible. + _ => return Err(Error::UNEXPECTED), + } + } + Ok(()) +} diff --git a/vendor/getrandom/src/backends/solid.rs b/vendor/getrandom/src/backends/solid.rs new file mode 100644 index 00000000..caa773f8 --- /dev/null +++ b/vendor/getrandom/src/backends/solid.rs @@ -0,0 +1,19 @@ +//! Implementation for SOLID +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +extern "C" { + pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> i32; +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr().cast::<u8>(), dest.len()) }; + if ret >= 0 { + Ok(()) + } else { + Err(Error::from_neg_error_code(ret)) + } +} diff --git a/vendor/getrandom/src/backends/use_file.rs b/vendor/getrandom/src/backends/use_file.rs new file mode 100644 index 00000000..7b48d433 --- /dev/null +++ b/vendor/getrandom/src/backends/use_file.rs @@ -0,0 +1,234 @@ +//! Implementations that just need to read from a file +use crate::Error; +use core::{ + ffi::c_void, + mem::MaybeUninit, + sync::atomic::{AtomicI32, Ordering}, +}; + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +pub(super) mod util_libc; + +/// For all platforms, we use `/dev/urandom` rather than `/dev/random`. +/// For more information see the linked man pages in lib.rs. +/// - On Linux, "/dev/urandom is preferred and sufficient in all use cases". +/// - On Redox, only /dev/urandom is provided. +/// - On AIX, /dev/urandom will "provide cryptographically secure output". +/// - On Haiku and QNX Neutrino they are identical. +const FILE_PATH: &[u8] = b"/dev/urandom\0"; + +// File descriptor is a "nonnegative integer", so we can safely use negative sentinel values. +const FD_UNINIT: libc::c_int = -1; +const FD_ONGOING_INIT: libc::c_int = -2; + +// In theory `libc::c_int` could be something other than `i32`, but for the +// targets we currently support that use `use_file`, it is always `i32`. +// If/when we add support for a target where that isn't the case, we may +// need to use a different atomic type or make other accomodations. The +// compiler will let us know if/when that is the case, because the +// `FD.store(fd)` would fail to compile. +// +// The opening of the file, by libc/libstd/etc. may write some unknown +// state into in-process memory. (Such state may include some sanitizer +// bookkeeping, or we might be operating in a unikernal-like environment +// where all the "kernel" file descriptor bookkeeping is done in our +// process.) `get_fd_locked` stores into FD using `Ordering::Release` to +// ensure any such state is synchronized. `get_fd` loads from `FD` with +// `Ordering::Acquire` to synchronize with it. +static FD: AtomicI32 = AtomicI32::new(FD_UNINIT); + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + let mut fd = FD.load(Ordering::Acquire); + if fd == FD_UNINIT || fd == FD_ONGOING_INIT { + fd = open_or_wait()?; + } + util_libc::sys_fill_exact(dest, |buf| unsafe { + libc::read(fd, buf.as_mut_ptr().cast::<c_void>(), buf.len()) + }) +} + +/// Open a file in read-only mode. +/// +/// # Panics +/// If `path` does not contain any zeros. +// TODO: Move `path` to `CStr` and use `CStr::from_bytes_until_nul` (MSRV 1.69) +// or C-string literals (MSRV 1.77) for statics +fn open_readonly(path: &[u8]) -> Result<libc::c_int, Error> { + assert!(path.contains(&0)); + loop { + let fd = unsafe { + libc::open( + path.as_ptr().cast::<libc::c_char>(), + libc::O_RDONLY | libc::O_CLOEXEC, + ) + }; + if fd >= 0 { + return Ok(fd); + } + let err = util_libc::last_os_error(); + // We should try again if open() was interrupted. + if err.raw_os_error() != Some(libc::EINTR) { + return Err(err); + } + } +} + +#[cold] +#[inline(never)] +fn open_or_wait() -> Result<libc::c_int, Error> { + loop { + match FD.load(Ordering::Acquire) { + FD_UNINIT => { + let res = FD.compare_exchange_weak( + FD_UNINIT, + FD_ONGOING_INIT, + Ordering::AcqRel, + Ordering::Relaxed, + ); + if res.is_ok() { + break; + } + } + FD_ONGOING_INIT => sync::wait(), + fd => return Ok(fd), + } + } + + let res = open_fd(); + let val = match res { + Ok(fd) => fd, + Err(_) => FD_UNINIT, + }; + FD.store(val, Ordering::Release); + + // On non-Linux targets `wait` is just 1 ms sleep, + // so we don't need any explicit wake up in addition + // to updating value of `FD`. + #[cfg(any(target_os = "android", target_os = "linux"))] + sync::wake(); + + res +} + +fn open_fd() -> Result<libc::c_int, Error> { + #[cfg(any(target_os = "android", target_os = "linux"))] + sync::wait_until_rng_ready()?; + let fd = open_readonly(FILE_PATH)?; + debug_assert!(fd >= 0); + Ok(fd) +} + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +mod sync { + /// Sleep 1 ms before checking `FD` again. + /// + /// On non-Linux targets the critical section only opens file, + /// which should not block, so in the unlikely contended case, + /// we can sleep-wait for the opening operation to finish. + pub(super) fn wait() { + let rqtp = libc::timespec { + tv_sec: 0, + tv_nsec: 1_000_000, + }; + let mut rmtp = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + // We do not care if sleep gets interrupted, so the return value is ignored + unsafe { + libc::nanosleep(&rqtp, &mut rmtp); + } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod sync { + use super::{open_readonly, util_libc::last_os_error, Error, FD, FD_ONGOING_INIT}; + + /// Wait for atomic `FD` to change value from `FD_ONGOING_INIT` to something else. + /// + /// Futex syscall with `FUTEX_WAIT` op puts the current thread to sleep + /// until futex syscall with `FUTEX_WAKE` op gets executed for `FD`. + /// + /// For more information read: https://www.man7.org/linux/man-pages/man2/futex.2.html + pub(super) fn wait() { + let op = libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG; + let timeout_ptr = core::ptr::null::<libc::timespec>(); + let ret = unsafe { libc::syscall(libc::SYS_futex, &FD, op, FD_ONGOING_INIT, timeout_ptr) }; + // FUTEX_WAIT should return either 0 or EAGAIN error + debug_assert!({ + match ret { + 0 => true, + -1 => last_os_error().raw_os_error() == Some(libc::EAGAIN), + _ => false, + } + }); + } + + /// Wake up all threads which wait for value of atomic `FD` to change. + pub(super) fn wake() { + let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG; + let ret = unsafe { libc::syscall(libc::SYS_futex, &FD, op, libc::INT_MAX) }; + debug_assert!(ret >= 0); + } + + // Polls /dev/random to make sure it is ok to read from /dev/urandom. + // + // Polling avoids draining the estimated entropy from /dev/random; + // short-lived processes reading even a single byte from /dev/random could + // be problematic if they are being executed faster than entropy is being + // collected. + // + // OTOH, reading a byte instead of polling is more compatible with + // sandboxes that disallow `poll()` but which allow reading /dev/random, + // e.g. sandboxes that assume that `poll()` is for network I/O. This way, + // fewer applications will have to insert pre-sandbox-initialization logic. + // Often (blocking) file I/O is not allowed in such early phases of an + // application for performance and/or security reasons. + // + // It is hard to write a sandbox policy to support `libc::poll()` because + // it may invoke the `poll`, `ppoll`, `ppoll_time64` (since Linux 5.1, with + // newer versions of glibc), and/or (rarely, and probably only on ancient + // systems) `select`. depending on the libc implementation (e.g. glibc vs + // musl), libc version, potentially the kernel version at runtime, and/or + // the target architecture. + // + // BoringSSL and libstd don't try to protect against insecure output from + // `/dev/urandom'; they don't open `/dev/random` at all. + // + // OpenSSL uses `libc::select()` unless the `dev/random` file descriptor + // is too large; if it is too large then it does what we do here. + // + // libsodium uses `libc::poll` similarly to this. + pub(super) fn wait_until_rng_ready() -> Result<(), Error> { + let fd = open_readonly(b"/dev/random\0")?; + let mut pfd = libc::pollfd { + fd, + events: libc::POLLIN, + revents: 0, + }; + + let res = loop { + // A negative timeout means an infinite timeout. + let res = unsafe { libc::poll(&mut pfd, 1, -1) }; + if res >= 0 { + // We only used one fd, and cannot timeout. + debug_assert_eq!(res, 1); + break Ok(()); + } + let err = last_os_error(); + // Assuming that `poll` is called correctly, + // on Linux it can return only EINTR and ENOMEM errors. + match err.raw_os_error() { + Some(libc::EINTR) => continue, + _ => break Err(err), + } + }; + unsafe { libc::close(fd) }; + res + } +} diff --git a/vendor/getrandom/src/backends/vxworks.rs b/vendor/getrandom/src/backends/vxworks.rs new file mode 100644 index 00000000..5f5e6773 --- /dev/null +++ b/vendor/getrandom/src/backends/vxworks.rs @@ -0,0 +1,54 @@ +//! Implementation for VxWorks +use crate::Error; +use core::{ + cmp::Ordering::{Equal, Greater, Less}, + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering::Relaxed}, +}; + +#[path = "../util_libc.rs"] +mod util_libc; + +pub use crate::util::{inner_u32, inner_u64}; + +static RNG_INIT: AtomicBool = AtomicBool::new(false); + +#[cold] +fn init() -> Result<(), Error> { + let ret = unsafe { libc::randSecure() }; + match ret.cmp(&0) { + Greater => RNG_INIT.store(true, Relaxed), + Equal => unsafe { + libc::usleep(10); + }, + Less => return Err(Error::VXWORKS_RAND_SECURE), + } + Ok(()) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + while !RNG_INIT.load(Relaxed) { + init()?; + } + + // Prevent overflow of i32 + let chunk_size = usize::try_from(i32::MAX).expect("VxWorks does not support 16-bit targets"); + for chunk in dest.chunks_mut(chunk_size) { + let chunk_len: libc::c_int = chunk + .len() + .try_into() + .expect("chunk size is bounded by i32::MAX"); + let p: *mut libc::c_uchar = chunk.as_mut_ptr().cast(); + let ret = unsafe { libc::randABytes(p, chunk_len) }; + if ret != 0 { + return Err(util_libc::last_os_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); +} diff --git a/vendor/getrandom/src/backends/wasi_p1.rs b/vendor/getrandom/src/backends/wasi_p1.rs new file mode 100644 index 00000000..25b5ca3b --- /dev/null +++ b/vendor/getrandom/src/backends/wasi_p1.rs @@ -0,0 +1,32 @@ +//! Implementation for WASI Preview 1 +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +// This linking is vendored from the wasi crate: +// https://docs.rs/wasi/0.11.0+wasi-snapshot-preview1/src/wasi/lib_generated.rs.html#2344-2350 +#[link(wasm_import_module = "wasi_snapshot_preview1")] +extern "C" { + fn random_get(arg0: i32, arg1: i32) -> i32; +} + +/// WASI p1 uses `u16` for error codes in its witx definitions: +/// https://github.com/WebAssembly/WASI/blob/38454e9e/legacy/preview1/witx/typenames.witx#L34-L39 +const MAX_ERROR_CODE: i32 = u16::MAX as i32; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + // Based on the wasi code: + // https://docs.rs/wasi/0.11.0+wasi-snapshot-preview1/src/wasi/lib_generated.rs.html#2046-2062 + // Note that size of an allocated object can not be bigger than isize::MAX bytes. + // WASI 0.1 supports only 32-bit WASM, so casting length to `i32` is safe. + #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] + let ret = unsafe { random_get(dest.as_mut_ptr() as i32, dest.len() as i32) }; + match ret { + 0 => Ok(()), + // WASI functions should return positive error codes which are smaller than `MAX_ERROR_CODE` + code if code <= MAX_ERROR_CODE => Err(Error::from_neg_error_code(-code)), + _ => Err(Error::UNEXPECTED), + } +} diff --git a/vendor/getrandom/src/backends/wasi_p2.rs b/vendor/getrandom/src/backends/wasi_p2.rs new file mode 100644 index 00000000..63bd2d7c --- /dev/null +++ b/vendor/getrandom/src/backends/wasi_p2.rs @@ -0,0 +1,50 @@ +//! Implementation for WASI Preview 2. +use crate::Error; +use core::mem::MaybeUninit; +use wasi::random::random::get_random_u64; + +#[inline] +pub fn inner_u32() -> Result<u32, Error> { + let val = get_random_u64(); + Ok(crate::util::truncate(val)) +} + +#[inline] +pub fn inner_u64() -> Result<u64, Error> { + Ok(get_random_u64()) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + use core::ptr::copy_nonoverlapping; + use wasi::random::random::get_random_u64; + + let (prefix, chunks, suffix) = unsafe { dest.align_to_mut::<MaybeUninit<u64>>() }; + + // We use `get_random_u64` instead of `get_random_bytes` because the latter creates + // an allocation due to the Wit IDL [restrictions][0]. This should be fine since + // the main use case of `getrandom` is seed generation. + // + // [0]: https://github.com/WebAssembly/wasi-random/issues/27 + if !prefix.is_empty() { + let val = get_random_u64(); + let src = (&val as *const u64).cast(); + unsafe { + copy_nonoverlapping(src, prefix.as_mut_ptr(), prefix.len()); + } + } + + for dst in chunks { + dst.write(get_random_u64()); + } + + if !suffix.is_empty() { + let val = get_random_u64(); + let src = (&val as *const u64).cast(); + unsafe { + copy_nonoverlapping(src, suffix.as_mut_ptr(), suffix.len()); + } + } + + Ok(()) +} diff --git a/vendor/getrandom/src/backends/wasm_js.rs b/vendor/getrandom/src/backends/wasm_js.rs new file mode 100644 index 00000000..1320d9fc --- /dev/null +++ b/vendor/getrandom/src/backends/wasm_js.rs @@ -0,0 +1,72 @@ +//! Implementation for WASM based on Web and Node.js +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +#[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<u8>]) -> 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<u8>]) -> 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<u8>]) -> 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); +} diff --git a/vendor/getrandom/src/backends/windows.rs b/vendor/getrandom/src/backends/windows.rs new file mode 100644 index 00000000..b5cd504f --- /dev/null +++ b/vendor/getrandom/src/backends/windows.rs @@ -0,0 +1,61 @@ +//! Implementation for Windows 10 and later +//! +//! On Windows 10 and later, ProcessPrng "is the primary interface to the +//! user-mode per-processor PRNGs" and only requires bcryptprimitives.dll, +//! making it a better option than the other Windows RNG APIs: +//! - BCryptGenRandom: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom +//! - Requires bcrypt.dll (which loads bcryptprimitives.dll anyway) +//! - Can cause crashes/hangs as BCrypt accesses the Windows Registry: +//! https://github.com/rust-lang/rust/issues/99341 +//! - Causes issues inside sandboxed code: +//! https://issues.chromium.org/issues/40277768 +//! - CryptGenRandom: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom +//! - Deprecated and not available on UWP targets +//! - Requires advapi32.lib/advapi32.dll (in addition to bcryptprimitives.dll) +//! - Thin wrapper around ProcessPrng +//! - RtlGenRandom: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom +//! - Deprecated and not available on UWP targets +//! - Requires advapi32.dll (in addition to bcryptprimitives.dll) +//! - Requires using name "SystemFunction036" +//! - Thin wrapper around ProcessPrng +//! +//! For more information see the Windows RNG Whitepaper: https://aka.ms/win10rng +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +// Binding to the Windows.Win32.Security.Cryptography.ProcessPrng API. As +// bcryptprimitives.dll lacks an import library, we use "raw-dylib". This +// was added in Rust 1.65 for x86_64/aarch64 and in Rust 1.71 for x86. +// We don't need MSRV 1.71, as we only use this backend on Rust 1.78 and later. +#[cfg_attr( + target_arch = "x86", + link( + name = "bcryptprimitives", + kind = "raw-dylib", + import_name_type = "undecorated" + ) +)] +#[cfg_attr( + not(target_arch = "x86"), + link(name = "bcryptprimitives", kind = "raw-dylib") +)] +extern "system" { + fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; +} +#[allow(clippy::upper_case_acronyms)] +type BOOL = core::ffi::c_int; // MSRV 1.64, similarly OK for this backend. +const TRUE: BOOL = 1; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + let result = unsafe { ProcessPrng(dest.as_mut_ptr().cast::<u8>(), dest.len()) }; + // Since Windows 10, calls to the user-mode RNG are guaranteed to never + // fail during runtime (rare windows W); `ProcessPrng` will only ever + // return 1 (which is how windows represents TRUE). + // See the bottom of page 6 of the aforementioned Windows RNG + // whitepaper for more information. + debug_assert!(result == TRUE); + Ok(()) +} diff --git a/vendor/getrandom/src/backends/windows7.rs b/vendor/getrandom/src/backends/windows7.rs new file mode 100644 index 00000000..8a353a9f --- /dev/null +++ b/vendor/getrandom/src/backends/windows7.rs @@ -0,0 +1,45 @@ +//! Legacy implementation for Windows XP and later +//! +//! For targets where we cannot use ProcessPrng (added in Windows 10), we use +//! RtlGenRandom. See windows.rs for a more detailed discussion of the Windows +//! RNG APIs (and why we don't use BCryptGenRandom). On versions prior to +//! Windows 10, this implementation is secure. On Windows 10 and later, this +//! implementation behaves identically to the windows.rs implementation, except +//! that it forces the loading of an additonal DLL (advapi32.dll). +//! +//! This implementation will not work on UWP targets (which lack advapi32.dll), +//! but such targets require Windows 10, so can use the standard implementation. +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +// Binding to the Windows.Win32.Security.Authentication.Identity.RtlGenRandom +// API. Don't use windows-targets as it doesn't support Windows 7 targets. +#[link(name = "advapi32")] +extern "system" { + #[link_name = "SystemFunction036"] + fn RtlGenRandom(randombuffer: *mut c_void, randombufferlength: u32) -> BOOLEAN; +} +#[allow(clippy::upper_case_acronyms)] +type BOOLEAN = u8; +const TRUE: BOOLEAN = 1u8; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { + // Prevent overflow of u32 + let chunk_size = usize::try_from(i32::MAX).expect("Windows does not support 16-bit targets"); + for chunk in dest.chunks_mut(chunk_size) { + let chunk_len = u32::try_from(chunk.len()).expect("chunk size is bounded by i32::MAX"); + let ret = unsafe { RtlGenRandom(chunk.as_mut_ptr().cast::<c_void>(), chunk_len) }; + if ret != TRUE { + return Err(Error::WINDOWS_RTL_GEN_RANDOM); + } + } + 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); +} |
