diff --git a/benches/mod.rs b/benches/mod.rs index 11be47eb..75b9ba09 100644 --- a/benches/mod.rs +++ b/benches/mod.rs @@ -1,59 +1,22 @@ #![feature(test)] extern crate test; -use std::{ - alloc::{alloc_zeroed, dealloc, Layout}, - ptr::NonNull, -}; - -// AlignedBuffer is like a Box<[u8; N]> except that it is always N-byte aligned -struct AlignedBuffer(NonNull<[u8; N]>); - -impl AlignedBuffer { - fn layout() -> Layout { - Layout::from_size_align(N, N).unwrap() - } - - fn new() -> Self { - let p = unsafe { alloc_zeroed(Self::layout()) } as *mut [u8; N]; - Self(NonNull::new(p).unwrap()) - } - - fn buf(&mut self) -> &mut [u8; N] { - unsafe { self.0.as_mut() } - } -} - -impl Drop for AlignedBuffer { - fn drop(&mut self) { - unsafe { dealloc(self.0.as_ptr() as *mut u8, Self::layout()) } - } -} - -// Used to benchmark the throughput of getrandom in an optimal scenario. -// The buffer is hot, and does not require initialization. #[inline(always)] fn bench(b: &mut test::Bencher) { - let mut ab = AlignedBuffer::::new(); - let buf = ab.buf(); b.iter(|| { + let mut buf = [0u8; N]; getrandom::getrandom(&mut buf[..]).unwrap(); test::black_box(&buf); }); b.bytes = N as u64; } -// Used to benchmark the throughput of getrandom is a slightly less optimal -// scenario. The buffer is still hot, but requires initialization. #[inline(always)] -fn bench_with_init(b: &mut test::Bencher) { - let mut ab = AlignedBuffer::::new(); - let buf = ab.buf(); +fn bench_raw(b: &mut test::Bencher) { b.iter(|| { - for byte in buf.iter_mut() { - *byte = 0; - } - getrandom::getrandom(&mut buf[..]).unwrap(); + let mut buf = core::mem::MaybeUninit::<[u8; N]>::uninit(); + // TODO: use `cast` on MSRV bump to 1.38 + unsafe { getrandom::getrandom_raw(buf.as_mut_ptr() as *mut u8, N).unwrap() }; test::black_box(&buf); }); b.bytes = N as u64; @@ -71,8 +34,8 @@ fn bench_seed(b: &mut test::Bencher) { bench::(b); } #[bench] -fn bench_seed_init(b: &mut test::Bencher) { - bench_with_init::(b); +fn bench_seed_raw(b: &mut test::Bencher) { + bench_raw::(b); } #[bench] @@ -80,8 +43,8 @@ fn bench_page(b: &mut test::Bencher) { bench::(b); } #[bench] -fn bench_page_init(b: &mut test::Bencher) { - bench_with_init::(b); +fn bench_page_raw(b: &mut test::Bencher) { + bench_raw::(b); } #[bench] @@ -89,6 +52,6 @@ fn bench_large(b: &mut test::Bencher) { bench::(b); } #[bench] -fn bench_large_init(b: &mut test::Bencher) { - bench_with_init::(b); +fn bench_large_raw(b: &mut test::Bencher) { + bench_raw::(b); } diff --git a/src/3ds.rs b/src/3ds.rs index 60305127..6dfb6f0a 100644 --- a/src/3ds.rs +++ b/src/3ds.rs @@ -10,8 +10,9 @@ use crate::util_libc::sys_fill_exact; use crate::Error; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - sys_fill_exact(dest, |buf| unsafe { - libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { + // TODO: use `cast` on MSRV bump to 1.38 + sys_fill_exact(dst, len, |dst, len| { + libc::getrandom(dst as *mut libc::c_void, len, 0) }) } diff --git a/src/bsd_arandom.rs b/src/bsd_arandom.rs index d4412125..d5bf1994 100644 --- a/src/bsd_arandom.rs +++ b/src/bsd_arandom.rs @@ -10,19 +10,17 @@ use crate::{util_libc::sys_fill_exact, Error}; use core::ptr; -fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { +unsafe fn kern_arnd(dst: *mut u8, mut len: usize) -> libc::ssize_t { static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND]; - let mut len = buf.len(); - let ret = unsafe { - libc::sysctl( - MIB.as_ptr(), - MIB.len() as libc::c_uint, - buf.as_mut_ptr() as *mut _, - &mut len, - ptr::null(), - 0, - ) - }; + // TODO: use `cast` on MSRV bump to 1.38 + let ret = libc::sysctl( + MIB.as_ptr(), + MIB.len() as libc::c_uint, + dst as *mut libc::c_void, + &mut len, + ptr::null(), + 0, + ); if ret == -1 { -1 } else { @@ -30,7 +28,7 @@ fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { } } -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(mut dst: *mut u8, mut len: usize) -> Result<(), Error> { // getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0 #[cfg(target_os = "freebsd")] { @@ -40,14 +38,17 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; if let Some(fptr) = GETRANDOM.ptr() { - let func: GetRandomFn = unsafe { core::mem::transmute(fptr) }; - return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) }); + let func: GetRandomFn = core::mem::transmute(fptr); + return sys_fill_exact(dst, len, |dst, len| func(dst, len, 0)); } } // Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and // older NetBSD kernels will fail on longer buffers. - for chunk in dest.chunks_mut(256) { - sys_fill_exact(chunk, kern_arnd)? + while len != 0 { + let chunk_len = core::cmp::min(len, 256); + sys_fill_exact(dst, chunk_len, |dst, len| kern_arnd(dst, len))?; + dst = dst.add(chunk_len); + len -= chunk_len; } Ok(()) } diff --git a/src/custom.rs b/src/custom.rs index 8432dfd2..5cd5c824 100644 --- a/src/custom.rs +++ b/src/custom.rs @@ -78,9 +78,9 @@ macro_rules! register_custom_getrandom { ($path:path) => { // We use an extern "C" function to get the guarantees of a stable ABI. #[no_mangle] - extern "C" fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 { + extern "C" fn __getrandom_custom(dst: *mut u8, len: usize) -> u32 { let f: fn(&mut [u8]) -> Result<(), $crate::Error> = $path; - let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) }; + let slice = unsafe { ::core::slice::from_raw_parts_mut(dst, len) }; match f(slice) { Ok(()) => 0, Err(e) => e.code().get(), @@ -90,11 +90,11 @@ macro_rules! register_custom_getrandom { } #[allow(dead_code)] -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { extern "C" { - fn __getrandom_custom(dest: *mut u8, len: usize) -> u32; + fn __getrandom_custom(dst: *mut u8, len: usize) -> u32; } - let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) }; + let ret = unsafe { __getrandom_custom(dst, len) }; match NonZeroU32::new(ret) { None => Ok(()), Some(code) => Err(Error::from(code)), diff --git a/src/dragonfly.rs b/src/dragonfly.rs index 8daaa404..64c9a27c 100644 --- a/src/dragonfly.rs +++ b/src/dragonfly.rs @@ -13,15 +13,15 @@ use crate::{ Error, }; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; // getrandom(2) was introduced in DragonflyBSD 5.7 if let Some(fptr) = GETRANDOM.ptr() { - let func: GetRandomFn = unsafe { core::mem::transmute(fptr) }; - return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) }); + let func: GetRandomFn = core::mem::transmute(fptr); + sys_fill_exact(dst, len, |dst, len| func(dst, len, 0)) } else { - use_file::getrandom_inner(dest) + use_file::getrandom_inner(dst, len) } } diff --git a/src/espidf.rs b/src/espidf.rs index dce8a2aa..ba85169b 100644 --- a/src/espidf.rs +++ b/src/espidf.rs @@ -14,13 +14,13 @@ extern "C" { fn esp_fill_random(buf: *mut c_void, len: usize) -> u32; } -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> 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()) }; - + // TODO: use `cast` on MSRV bump to 1.38 + esp_fill_random(dst as *mut c_void, len); Ok(()) } diff --git a/src/fuchsia.rs b/src/fuchsia.rs index 572ff534..649541ad 100644 --- a/src/fuchsia.rs +++ b/src/fuchsia.rs @@ -14,7 +14,7 @@ extern "C" { fn zx_cprng_draw(buffer: *mut u8, length: usize); } -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) } +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { + zx_cprng_draw(dst, len); Ok(()) } diff --git a/src/ios.rs b/src/ios.rs index 226de16b..53cdf930 100644 --- a/src/ios.rs +++ b/src/ios.rs @@ -15,9 +15,9 @@ extern "C" { fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> i32; } -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { // Apple's documentation guarantees kSecRandomDefault is a synonym for NULL. - let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr()) }; + let ret = SecRandomCopyBytes(null(), len, dst); // errSecSuccess (from SecBase.h) is always zero. if ret != 0 { Err(Error::IOS_SEC_RANDOM) diff --git a/src/lib.rs b/src/lib.rs index d21a88ff..81cf4055 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -251,7 +251,7 @@ cfg_if! { } } -/// Fill `dest` with random bytes from the system's preferred random number +/// Fill `dst` with random bytes from the system's preferred random number /// source. /// /// This function returns an error on any failure, including partial reads. We @@ -264,9 +264,39 @@ cfg_if! { /// 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). -pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { - if dest.is_empty() { +pub fn getrandom(dst: &mut [u8]) -> Result<(), Error> { + if dst.is_empty() { return Ok(()); } - imp::getrandom_inner(dest) + unsafe { getrandom_raw(dst.as_mut_ptr(), dst.len()) } +} + +/// Raw version of the `getrandom` function. +/// +/// If this function returns `Ok(())`, then it's safe to assume that the +/// `len` random bytes were written to the memory pointed by `dst`. +/// +/// # Safety +/// +/// `dst` MUST be [valid] for writes of `len` bytes. +/// +/// [valid]: core::ptr#safety +/// +/// # Examples +/// +/// ```ignore +/// # // We ignore this doctest because `MaybeUninit` was stabilized +/// # // in Rust 1.36, while this crate has MSRV equal to 1.34. +/// # fn main() -> Result<(), getrandom::Error> { +/// const BUF_SIZE: usize = 1024; +/// +/// let buf: [u8; BUF_SIZE] = unsafe { +/// let mut buf = core::mem::MaybeUninit::uninit(); +/// getrandom::getrandom_raw(buf.as_mut_ptr() as *mut u8, BUF_SIZE)?; +/// buf.assume_init() +/// }; +/// # Ok(()) } +/// ``` +pub unsafe fn getrandom_raw(dst: *mut u8, len: usize) -> Result<(), Error> { + imp::getrandom_inner(dst, len) } diff --git a/src/linux_android.rs b/src/linux_android.rs index 4270b67c..4976c1c7 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -13,15 +13,16 @@ use crate::{ {use_file, Error}, }; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { // getrandom(2) was introduced in Linux 3.17 static HAS_GETRANDOM: LazyBool = LazyBool::new(); if HAS_GETRANDOM.unsync_init(is_getrandom_available) { - sys_fill_exact(dest, |buf| unsafe { - getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) + // TODO: use `cast` on MSRV bump to 1.38 + sys_fill_exact(dst, len, |dst, len| { + getrandom(dst as *mut libc::c_void, len, 0) }) } else { - use_file::getrandom_inner(dest) + use_file::getrandom_inner(dst, len) } } diff --git a/src/macos.rs b/src/macos.rs index 671a053b..5a06d370 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -16,21 +16,24 @@ use core::mem; type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(mut dst: *mut u8, mut len: usize) -> Result<(), Error> { // getentropy(2) was added in 10.12, Rust supports 10.7+ static GETENTROPY: Weak = unsafe { Weak::new("getentropy\0") }; if let Some(fptr) = GETENTROPY.ptr() { - let func: GetEntropyFn = unsafe { mem::transmute(fptr) }; - for chunk in dest.chunks_mut(256) { - let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) }; + let func: GetEntropyFn = mem::transmute(fptr); + while len != 0 { + let chunk_len = core::cmp::min(len, 256); + let ret = func(dst, chunk_len); if ret != 0 { return Err(last_os_error()); } + dst = dst.add(chunk_len); + len -= chunk_len; } Ok(()) } else { // We fallback to reading from /dev/random instead of SecRandomCopyBytes // to avoid high startup costs and linking the Security framework. - use_file::getrandom_inner(dest) + use_file::getrandom_inner(dst, len) } } diff --git a/src/openbsd.rs b/src/openbsd.rs index 41371736..7e53c0ce 100644 --- a/src/openbsd.rs +++ b/src/openbsd.rs @@ -9,13 +9,17 @@ //! Implementation for OpenBSD use crate::{util_libc::last_os_error, Error}; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(mut dst: *mut u8, mut len: usize) -> Result<(), Error> { // getentropy(2) was added in OpenBSD 5.6, so we can use it unconditionally. - for chunk in dest.chunks_mut(256) { - let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) }; + while len != 0 { + let chunk_len = core::cmp::min(len, 256); + // TODO: use `cast` on MSRV bump to 1.38 + let ret = libc::getentropy(dst as *mut libc::c_void, chunk_len); if ret == -1 { return Err(last_os_error()); } + dst = dst.add(chunk_len); + len -= chunk_len; } Ok(()) } diff --git a/src/rdrand.rs b/src/rdrand.rs index 1df21e5d..63a0e5a6 100644 --- a/src/rdrand.rs +++ b/src/rdrand.rs @@ -8,7 +8,7 @@ //! Implementation for SGX using RDRAND instruction use crate::Error; -use core::mem; +use core::{mem, ptr}; cfg_if! { if #[cfg(target_arch = "x86_64")] { @@ -69,29 +69,31 @@ fn is_rdrand_supported() -> bool { HAS_RDRAND.unsync_init(|| unsafe { (arch::__cpuid(1).ecx & FLAG) != 0 }) } -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { if !is_rdrand_supported() { return Err(Error::NO_RDRAND); } // SAFETY: After this point, rdrand is supported, so calling the rdrand // functions is not undefined behavior. - unsafe { rdrand_exact(dest) } + rdrand_exact(dst, len) } #[target_feature(enable = "rdrand")] -unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> { - // 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(WORD_SIZE); - for chunk in chunks.by_ref() { - chunk.copy_from_slice(&rdrand()?); +unsafe fn rdrand_exact(mut dst: *mut u8, mut len: usize) -> Result<(), Error> { + while len >= WORD_SIZE { + // TODO: use `cast` on MSRV bump to 1.38 + ptr::write(dst as *mut [u8; WORD_SIZE], rdrand()?); + dst = dst.add(WORD_SIZE); + len -= WORD_SIZE; } - let tail = chunks.into_remainder(); - let n = tail.len(); - if n > 0 { - tail.copy_from_slice(&rdrand()?[..n]); + if len != 0 { + let src = rdrand()?; + // TODO: use `cast` on MSRV bump to 1.38 + let src_ptr = &src as *const [u8; WORD_SIZE] as *const u8; + ptr::copy_nonoverlapping(src_ptr, dst, len) } + Ok(()) } diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs index cf3067d6..cafc3e3c 100644 --- a/src/solaris_illumos.rs +++ b/src/solaris_illumos.rs @@ -29,20 +29,23 @@ type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> #[cfg(target_os = "solaris")] type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::c_int; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(mut dst: &mut [u8], mut len: usize) -> Result<(), Error> { // getrandom(2) was introduced in Solaris 11.3 for Illumos in 2015. static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; if let Some(fptr) = GETRANDOM.ptr() { - let func: GetRandomFn = unsafe { mem::transmute(fptr) }; + let func: GetRandomFn = mem::transmute(fptr); // 256 bytes is the lowest common denominator across all the Solaris // derived platforms for atomically obtaining random data. - for chunk in dest.chunks_mut(256) { - sys_fill_exact(chunk, |buf| unsafe { - func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t - })? + while len != 0 { + let chunk_len = core::cmp::min(len, 256); + sys_fill_exact(dst, chunk_len, |dst, len| { + func(dst, len, 0) as libc::ssize_t + })?; + dst = dst.add(chunk_len); + len -= chunk_len; } Ok(()) } else { - use_file::getrandom_inner(dest) + use_file::getrandom_inner(dst, len) } } diff --git a/src/solid.rs b/src/solid.rs index dc76aacb..d3123ec0 100644 --- a/src/solid.rs +++ b/src/solid.rs @@ -14,8 +14,8 @@ extern "C" { pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> i32; } -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr(), dest.len()) }; +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { + let ret = SOLID_RNG_SampleRandomBytes(dst, len); if ret >= 0 { Ok(()) } else { diff --git a/src/use_file.rs b/src/use_file.rs index 16c0216b..d83d0b65 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -29,17 +29,22 @@ const FILE_PATH: &str = "/dev/random\0"; #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] const FILE_PATH: &str = "/dev/urandom\0"; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { let fd = get_rng_fd()?; - let read = |buf: &mut [u8]| unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) }; + let read = |dst: *mut u8, len: usize| libc::read(fd, dst as *mut _, len); if cfg!(target_os = "emscripten") { - // `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes. - for chunk in dest.chunks_mut(65536) { - sys_fill_exact(chunk, read)?; + // `Crypto.getRandomValues` documents `len` should be at most 65536 bytes. + let mut dst = dst; + let mut len = len; + while len != 0 { + let chunk_len = core::cmp::min(len, 65536); + sys_fill_exact(dst, chunk_len, read)?; + dst = dst.add(chunk_len); + len -= chunk_len; } } else { - sys_fill_exact(dest, read)?; + sys_fill_exact(dst, len, read)?; } Ok(()) } diff --git a/src/util_libc.rs b/src/util_libc.rs index d057071a..e0c106ed 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -58,12 +58,13 @@ pub fn last_os_error() -> Error { // Fill a buffer by repeatedly invoking a system call. The `sys_fill` function: // - should return -1 and set errno on failure // - should return the number of bytes written on success -pub fn sys_fill_exact( - mut buf: &mut [u8], - sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t, +pub unsafe fn sys_fill_exact( + mut dst: *mut u8, + mut len: usize, + sys_fill: impl Fn(*mut u8, usize) -> libc::ssize_t, ) -> Result<(), Error> { - while !buf.is_empty() { - let res = sys_fill(buf); + while len != 0 { + let res = sys_fill(dst, len); if res < 0 { let err = last_os_error(); // We should try again if the call was interrupted. @@ -73,7 +74,9 @@ pub fn sys_fill_exact( } else { // We don't check for EOF (ret = 0) as the data we are reading // should be an infinite stream of random bytes. - buf = &mut buf[(res as usize)..]; + let res = res as usize; + dst = dst.add(res); + len -= res; } } Ok(()) diff --git a/src/vxworks.rs b/src/vxworks.rs index 6cb5d52f..484dab71 100644 --- a/src/vxworks.rs +++ b/src/vxworks.rs @@ -10,25 +10,28 @@ use crate::{util_libc::last_os_error, Error}; use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(mut dst: *mut u8, mut len: usize) -> Result<(), Error> { static RNG_INIT: AtomicBool = AtomicBool::new(false); while !RNG_INIT.load(Relaxed) { - let ret = unsafe { libc::randSecure() }; + let ret = libc::randSecure(); if ret < 0 { return Err(Error::VXWORKS_RAND_SECURE); } else if ret > 0 { RNG_INIT.store(true, Relaxed); break; } - unsafe { libc::usleep(10) }; + libc::usleep(10); } // Prevent overflow of i32 - for chunk in dest.chunks_mut(i32::max_value() as usize) { - let ret = unsafe { libc::randABytes(chunk.as_mut_ptr(), chunk.len() as i32) }; + while len != 0 { + let chunk_len = core::cmp::min(len, i32::max_value() as usize); + let ret = libc::randABytes(dst, chunk_len as i32); if ret != 0 { return Err(last_os_error()); } + dst = dst.add(chunk_len); + len -= chunk_len; } Ok(()) } diff --git a/src/wasi.rs b/src/wasi.rs index c5121824..d8c977f6 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -11,9 +11,9 @@ use crate::Error; use core::num::NonZeroU32; use wasi::wasi_snapshot_preview1::random_get; -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - match unsafe { random_get(dest.as_mut_ptr() as i32, dest.len() as i32) } { +pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { + match random_get(dst as i32, len as i32) { 0 => Ok(()), - err => Err(unsafe { NonZeroU32::new_unchecked(err as u32) }.into()), + err => Err(NonZeroU32::new_unchecked(err as u32).into()), } } diff --git a/src/windows.rs b/src/windows.rs index 41dc37a5..3405428d 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -21,18 +21,17 @@ extern "system" { ) -> u32; } -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { +pub unsafe fn getrandom_inner(mut dst: *mut u8, mut len: usize) -> Result<(), Error> { // Prevent overflow of u32 - for chunk in dest.chunks_mut(u32::max_value() as usize) { + while len != 0 { + let chunk_len = core::cmp::min(len, u32::max_value() as usize); // BCryptGenRandom was introduced in Windows Vista - let ret = unsafe { - BCryptGenRandom( - ptr::null_mut(), - chunk.as_mut_ptr(), - chunk.len() as u32, - BCRYPT_USE_SYSTEM_PREFERRED_RNG, - ) - }; + let ret = BCryptGenRandom( + ptr::null_mut(), + dst, + chunk_len as u32, + BCRYPT_USE_SYSTEM_PREFERRED_RNG, + ); // NTSTATUS codes use the two highest bits for severity status. if ret >> 30 == 0b11 { // We zeroize the highest bit, so the error code will reside @@ -41,9 +40,12 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { // SAFETY: the second highest bit is always equal to one, // so it's impossible to get zero. Unfortunately the type // system does not have a way to express this yet. - let code = unsafe { NonZeroU32::new_unchecked(code) }; + let code = NonZeroU32::new_unchecked(code); return Err(Error::from(code)); } + + dst = dst.add(chunk_len); + len -= chunk_len; } Ok(()) } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 006f230d..b2cadcbe 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,4 +1,4 @@ -use super::getrandom_impl; +use super::getrandom; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; @@ -9,16 +9,16 @@ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[test] fn test_zero() { // Test that APIs are happy with zero-length requests - getrandom_impl(&mut [0u8; 0]).unwrap(); + getrandom(&mut [0u8; 0]).unwrap(); } #[test] fn test_diff() { let mut v1 = [0u8; 1000]; - getrandom_impl(&mut v1).unwrap(); + getrandom(&mut v1).unwrap(); let mut v2 = [0u8; 1000]; - getrandom_impl(&mut v2).unwrap(); + getrandom(&mut v2).unwrap(); let mut n_diff_bits = 0; for i in 0..v1.len() { @@ -32,7 +32,7 @@ fn test_diff() { #[test] fn test_huge() { let mut huge = [0u8; 100_000]; - getrandom_impl(&mut huge).unwrap(); + getrandom(&mut huge).unwrap(); } // On WASM, the thread API always fails/panics @@ -53,7 +53,7 @@ fn test_multithreading() { let mut v = [0u8; 1000]; for _ in 0..100 { - getrandom_impl(&mut v).unwrap(); + getrandom(&mut v).unwrap(); thread::yield_now(); } }); diff --git a/tests/normal.rs b/tests/normal.rs index 5fff13b3..2392efd7 100644 --- a/tests/normal.rs +++ b/tests/normal.rs @@ -7,5 +7,5 @@ )))] // Use the normal getrandom implementation on this architecture. -use getrandom::getrandom as getrandom_impl; +use getrandom::getrandom; mod common; diff --git a/tests/rdrand.rs b/tests/rdrand.rs index 4ff85c47..57bc4fc8 100644 --- a/tests/rdrand.rs +++ b/tests/rdrand.rs @@ -11,5 +11,11 @@ mod rdrand; #[path = "../src/util.rs"] mod util; -use rdrand::getrandom_inner as getrandom_impl; +pub fn getrandom(dst: &mut [u8]) -> Result<(), Error> { + if dst.is_empty() { + return Ok(()); + } + unsafe { rdrand::getrandom_inner(dst.as_mut_ptr(), dst.len()) } +} + mod common;