From 07d6edf38c147c65c9424a64e730a04ee33f9b37 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 14:24:19 -0700 Subject: [PATCH 1/9] Increase MSRV to 1.38 so we can use safer pointer casts. --- .clippy.toml | 2 +- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 7 +++++++ README.md | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.clippy.toml b/.clippy.toml index 992016c2..749c3b58 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -msrv = "1.36" +msrv = "1.38" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 40db0124..6847a4e7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: strategy: matrix: os: [ubuntu-22.04, windows-2022] - toolchain: [nightly, beta, stable, 1.36] + toolchain: [nightly, beta, stable, 1.38] # Only Test macOS on stable to reduce macOS CI jobs include: # x86_64-apple-darwin. diff --git a/CHANGELOG.md b/CHANGELOG.md index b6763b58..d32a369e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Breaking Changes +- Update MSRV to 1.38 [#425] + +[#425]: https://github.com/rust-random/getrandom/pull/425 + ## [0.2.15] - 2024-05-06 ### Added - Apple visionOS support [#410] diff --git a/README.md b/README.md index b4b5a2b5..fcebbafa 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ crate features, WASM support and Custom RNGs see the ## Minimum Supported Rust Version -This crate requires Rust 1.36.0 or later. +This crate requires Rust 1.38.0 or later. ## Platform Support From 398b5d7fa8737e0fe91bd86a093bd7c465e8cc54 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 12:54:00 -0700 Subject: [PATCH 2/9] Use `p.cast::()` instead of as `as *mut T`. --- src/apple-other.rs | 2 +- src/error.rs | 2 +- src/fuchsia.rs | 2 +- src/getentropy.rs | 4 ++-- src/getrandom.rs | 4 ++-- src/hermit.rs | 2 +- src/js.rs | 4 ++-- src/netbsd.rs | 6 +++--- src/solaris.rs | 4 ++-- src/solid.rs | 2 +- src/use_file.rs | 3 ++- src/util_libc.rs | 2 +- src/vxworks.rs | 2 +- src/wasi.rs | 2 +- src/windows.rs | 7 ++++--- 15 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/apple-other.rs b/src/apple-other.rs index 167d8cf0..e54b4cb2 100644 --- a/src/apple-other.rs +++ b/src/apple-other.rs @@ -14,7 +14,7 @@ extern "C" { } pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - let ret = unsafe { CCRandomGenerateBytes(dest.as_mut_ptr() as *mut c_void, dest.len()) }; + let ret = unsafe { CCRandomGenerateBytes(dest.as_mut_ptr().cast::(), dest.len()) }; // kCCSuccess (from CommonCryptoError.h) is always zero. if ret != 0 { Err(Error::IOS_SEC_RANDOM) diff --git a/src/error.rs b/src/error.rs index 13c81c7a..5dc45110 100644 --- a/src/error.rs +++ b/src/error.rs @@ -99,7 +99,7 @@ impl Error { cfg_if! { if #[cfg(unix)] { fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> { - let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; + let buf_ptr = buf.as_mut_ptr().cast::(); if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { return None; } diff --git a/src/fuchsia.rs b/src/fuchsia.rs index 11970685..22ae7796 100644 --- a/src/fuchsia.rs +++ b/src/fuchsia.rs @@ -8,6 +8,6 @@ extern "C" { } pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - unsafe { zx_cprng_draw(dest.as_mut_ptr() as *mut u8, dest.len()) } + unsafe { zx_cprng_draw(dest.as_mut_ptr().cast::(), dest.len()) } Ok(()) } diff --git a/src/getentropy.rs b/src/getentropy.rs index 41bab8fe..eee47403 100644 --- a/src/getentropy.rs +++ b/src/getentropy.rs @@ -8,11 +8,11 @@ //! //! For these targets, we use getentropy(2) because getrandom(2) doesn't exist. use crate::{util_libc::last_os_error, Error}; -use core::mem::MaybeUninit; +use core::{ffi::c_void, mem::MaybeUninit}; pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { for chunk in dest.chunks_mut(256) { - let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) }; + let ret = unsafe { libc::getentropy(chunk.as_mut_ptr().cast::(), chunk.len()) }; if ret != 0 { return Err(last_os_error()); } diff --git a/src/getrandom.rs b/src/getrandom.rs index bc583653..88c077c6 100644 --- a/src/getrandom.rs +++ b/src/getrandom.rs @@ -16,10 +16,10 @@ //! nothing. On illumos, the default pool is used to implement getentropy(2), //! so we assume it is acceptable here. use crate::{util_libc::sys_fill_exact, Error}; -use core::mem::MaybeUninit; +use core::{ffi::c_void, mem::MaybeUninit}; pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { sys_fill_exact(dest, |buf| unsafe { - libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) + libc::getrandom(buf.as_mut_ptr().cast::(), buf.len(), 0) }) } diff --git a/src/hermit.rs b/src/hermit.rs index c4f61941..d797f74a 100644 --- a/src/hermit.rs +++ b/src/hermit.rs @@ -13,7 +13,7 @@ extern "C" { pub fn getrandom_inner(mut dest: &mut [MaybeUninit]) -> Result<(), Error> { while !dest.is_empty() { - let res = unsafe { sys_read_entropy(dest.as_mut_ptr() as *mut u8, dest.len(), 0) }; + let res = unsafe { sys_read_entropy(dest.as_mut_ptr().cast::(), dest.len(), 0) }; // Positive `isize`s can be safely casted to `usize` if res > 0 && (res as usize) <= dest.len() { dest = &mut dest[res as usize..]; diff --git a/src/js.rs b/src/js.rs index e5428f50..bec31ee5 100644 --- a/src/js.rs +++ b/src/js.rs @@ -40,7 +40,7 @@ pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> // have a notion of "uninitialized memory", this is purely // a Rust/C/C++ concept. let res = n.random_fill_sync(unsafe { - Uint8Array::view_mut_raw(chunk.as_mut_ptr() as *mut u8, chunk.len()) + Uint8Array::view_mut_raw(chunk.as_mut_ptr().cast::(), chunk.len()) }); if res.is_err() { return Err(Error::NODE_RANDOM_FILL_SYNC); @@ -60,7 +60,7 @@ pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> } // SAFETY: `sub_buf`'s length is the same length as `chunk` - unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr() as *mut u8) }; + unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::()) }; } } }; diff --git a/src/netbsd.rs b/src/netbsd.rs index b8a770f5..34c77837 100644 --- a/src/netbsd.rs +++ b/src/netbsd.rs @@ -3,7 +3,7 @@ use crate::{ util_libc::{sys_fill_exact, Weak}, Error, }; -use core::{mem::MaybeUninit, ptr}; +use core::{ffi::c_void, mem::MaybeUninit, ptr}; fn kern_arnd(buf: &mut [MaybeUninit]) -> libc::ssize_t { static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND]; @@ -12,7 +12,7 @@ fn kern_arnd(buf: &mut [MaybeUninit]) -> libc::ssize_t { libc::sysctl( MIB.as_ptr(), MIB.len() as libc::c_uint, - buf.as_mut_ptr() as *mut _, + buf.as_mut_ptr().cast::(), &mut len, ptr::null(), 0, @@ -33,7 +33,7 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { 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() as *mut u8, buf.len(), 0) + func(buf.as_mut_ptr().cast::(), buf.len(), 0) }); } diff --git a/src/solaris.rs b/src/solaris.rs index 8a3401e0..c96ae2a3 100644 --- a/src/solaris.rs +++ b/src/solaris.rs @@ -13,13 +13,13 @@ //! 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::{util_libc::last_os_error, Error}; -use core::mem::MaybeUninit; +use core::{ffi::c_void, mem::MaybeUninit}; const MAX_BYTES: usize = 1024; pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { for chunk in dest.chunks_mut(MAX_BYTES) { - let ptr = chunk.as_mut_ptr() as *mut libc::c_void; + let ptr = chunk.as_mut_ptr().cast::(); 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 ret <= 0 { diff --git a/src/solid.rs b/src/solid.rs index cae8caf6..56a47eeb 100644 --- a/src/solid.rs +++ b/src/solid.rs @@ -7,7 +7,7 @@ extern "C" { } pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr() as *mut u8, dest.len()) }; + let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr().cast::(), dest.len()) }; if ret >= 0 { Ok(()) } else { diff --git a/src/use_file.rs b/src/use_file.rs index bd643ae5..d2fe244f 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -5,6 +5,7 @@ use crate::{ }; use core::{ cell::UnsafeCell, + ffi::c_void, mem::MaybeUninit, sync::atomic::{AtomicUsize, Ordering::Relaxed}, }; @@ -21,7 +22,7 @@ const FD_UNINIT: usize = usize::max_value(); pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { let fd = get_rng_fd()?; sys_fill_exact(dest, |buf| unsafe { - libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len()) + libc::read(fd, buf.as_mut_ptr().cast::(), buf.len()) }) } diff --git a/src/util_libc.rs b/src/util_libc.rs index 129362d5..e882ad82 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -154,7 +154,7 @@ pub fn getrandom_syscall(buf: &mut [MaybeUninit]) -> libc::ssize_t { unsafe { libc::syscall( libc::SYS_getrandom, - buf.as_mut_ptr() as *mut libc::c_void, + buf.as_mut_ptr().cast::(), buf.len(), 0, ) as libc::ssize_t diff --git a/src/vxworks.rs b/src/vxworks.rs index 7ca9d6bf..5c75847b 100644 --- a/src/vxworks.rs +++ b/src/vxworks.rs @@ -20,7 +20,7 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { // Prevent overflow of i32 for chunk in dest.chunks_mut(i32::max_value() as usize) { - let ret = unsafe { libc::randABytes(chunk.as_mut_ptr() as *mut u8, chunk.len() as i32) }; + let ret = unsafe { libc::randABytes(chunk.as_mut_ptr().cast::(), chunk.len() as i32) }; if ret != 0 { return Err(last_os_error()); } diff --git a/src/wasi.rs b/src/wasi.rs index d6c8a912..a5b23070 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -7,7 +7,7 @@ use core::{ use wasi::random_get; pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - unsafe { random_get(dest.as_mut_ptr() as *mut u8, dest.len()) }.map_err(|e| { + unsafe { random_get(dest.as_mut_ptr().cast::(), dest.len()) }.map_err(|e| { // The WASI errno will always be non-zero, but we check just in case. match NonZeroU16::new(e.raw()) { Some(r) => Error::from(NonZeroU32::from(r)), diff --git a/src/windows.rs b/src/windows.rs index 2d1c4835..0aa8dde2 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -29,7 +29,7 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { let ret = unsafe { BCryptGenRandom( ptr::null_mut(), - chunk.as_mut_ptr() as *mut u8, + chunk.as_mut_ptr().cast::(), chunk.len() as u32, BCRYPT_USE_SYSTEM_PREFERRED_RNG, ) @@ -39,8 +39,9 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { // Failed. Try RtlGenRandom as a fallback. #[cfg(not(target_vendor = "uwp"))] { - let ret = - unsafe { RtlGenRandom(chunk.as_mut_ptr() as *mut c_void, chunk.len() as u32) }; + let ret = unsafe { + RtlGenRandom(chunk.as_mut_ptr().cast::(), chunk.len() as u32) + }; if ret != 0 { continue; } From d77e0009a560104c66c50780eb5bd369b0e58cb1 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 13:37:25 -0700 Subject: [PATCH 3/9] Clarify hazards in 'as *const _` casts of `*const u8` to `*const libc::c_char`. --- src/util_libc.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/util_libc.rs b/src/util_libc.rs index e882ad82..a447f424 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -117,7 +117,8 @@ impl Weak { // the use of non-Relaxed operations is probably unnecessary. match self.addr.load(Ordering::Relaxed) { Self::UNINIT => { - let symbol = self.name.as_ptr() as *const _; + // XXX/FIXME: Unchecked UTF-8-to-c_char cast. + let symbol = self.name.as_ptr().cast::(); let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, symbol) }; // Synchronizes with the Acquire fence below self.addr.store(addr, Ordering::Release); @@ -136,7 +137,11 @@ impl Weak { pub unsafe fn open_readonly(path: &str) -> Result { debug_assert_eq!(path.as_bytes().last(), Some(&0)); loop { - let fd = libc::open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC); + // XXX/FIXME: Unchecked UTF-8-to-c_char cast. + let fd = libc::open( + path.as_ptr().cast::(), + libc::O_RDONLY | libc::O_CLOEXEC, + ); if fd >= 0 { return Ok(fd); } From b5c89a6751c25fa909feab33f58836ecebdaead9 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 13:53:34 -0700 Subject: [PATCH 4/9] Minimize safety hazards in `slice_as_uninit[_mut]`. In each function, explicitly use the implicit conversion of `&[T]` to `*const T`/`&mut [T]` to `* mut T` to eliminate one `as *const T`/`as *mut T` respectively. Instead of relying on the remaining cast in each function being valid for all `T`, reduce the scope of the assumption to `u8`, and add a TODO avoid eliminating the use of the assumption. --- src/util.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/util.rs b/src/util.rs index 1c4e70ba..72d3e8fc 100644 --- a/src/util.rs +++ b/src/util.rs @@ -17,11 +17,14 @@ pub fn uninit_slice_fill_zero(slice: &mut [MaybeUninit]) -> &mut [u8] { } #[inline(always)] -pub fn slice_as_uninit(slice: &[T]) -> &[MaybeUninit] { - // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. - // There is no risk of writing a `MaybeUninit` into the result since +pub fn slice_as_uninit(slice: &[u8]) -> &[MaybeUninit] { + // TODO: MSRV(1.76): Use `core::ptr::from_ref`. + let ptr: *const [u8] = slice; + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `u8`. + // There is no risk of writing a `MaybeUninit` into the result since // the result isn't mutable. - unsafe { &*(slice as *const [T] as *const [MaybeUninit]) } + // FIXME: Avoid relying on this assumption and eliminate this cast. + unsafe { &*(ptr as *const [MaybeUninit]) } } /// View an mutable initialized array as potentially-uninitialized. @@ -29,7 +32,10 @@ pub fn slice_as_uninit(slice: &[T]) -> &[MaybeUninit] { /// This is unsafe because it allows assigning uninitialized values into /// `slice`, which would be undefined behavior. #[inline(always)] -pub unsafe fn slice_as_uninit_mut(slice: &mut [T]) -> &mut [MaybeUninit] { - // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. - &mut *(slice as *mut [T] as *mut [MaybeUninit]) +pub unsafe fn slice_as_uninit_mut(slice: &mut [u8]) -> &mut [MaybeUninit] { + // TODO: MSRV(1.76): Use `core::ptr::from_mut`. + let ptr: *mut [u8] = slice; + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `u8`. + // FIXME: Avoid relying on this assumption and eliminate this cast. + &mut *(ptr as *mut [MaybeUninit]) } From 68739449b8fd265d04a0b556f8c40b681b7b9e73 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 15:21:35 -0700 Subject: [PATCH 5/9] Increase MSRV to 1.42 so we can use slice patterns. --- .clippy.toml | 2 +- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 3 ++- README.md | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.clippy.toml b/.clippy.toml index 749c3b58..4b304069 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -msrv = "1.38" +msrv = "1.42" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6847a4e7..43786753 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: strategy: matrix: os: [ubuntu-22.04, windows-2022] - toolchain: [nightly, beta, stable, 1.38] + toolchain: [nightly, beta, stable, 1.42] # Only Test macOS on stable to reduce macOS CI jobs include: # x86_64-apple-darwin. diff --git a/CHANGELOG.md b/CHANGELOG.md index d32a369e..b7b46d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Breaking Changes -- Update MSRV to 1.38 [#425] +- Update MSRV to 1.42 [#425], [#426] [#425]: https://github.com/rust-random/getrandom/pull/425 +[#426]: https://github.com/rust-random/getrandom/pull/426 ## [0.2.15] - 2024-05-06 ### Added diff --git a/README.md b/README.md index fcebbafa..1aad16c8 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ crate features, WASM support and Custom RNGs see the ## Minimum Supported Rust Version -This crate requires Rust 1.38.0 or later. +This crate requires Rust 1.42.0 or later. ## Platform Support From e0d1f271f5d1020009e4c2ef783f1ab5b1423e72 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 15:34:30 -0700 Subject: [PATCH 6/9] Bump MSRV to 1.46 so we can use `if` in `const fn`. --- .clippy.toml | 2 +- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.clippy.toml b/.clippy.toml index 4b304069..eb66960a 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -msrv = "1.42" +msrv = "1.46" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 43786753..2171c39e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: strategy: matrix: os: [ubuntu-22.04, windows-2022] - toolchain: [nightly, beta, stable, 1.42] + toolchain: [nightly, beta, stable, 1.46] # Only Test macOS on stable to reduce macOS CI jobs include: # x86_64-apple-darwin. diff --git a/CHANGELOG.md b/CHANGELOG.md index b7b46d4e..ba03e74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Breaking Changes -- Update MSRV to 1.42 [#425], [#426] +- Update MSRV to 1.46 [#425], [#426] [#425]: https://github.com/rust-random/getrandom/pull/425 [#426]: https://github.com/rust-random/getrandom/pull/426 diff --git a/README.md b/README.md index 1aad16c8..07d6cbbd 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ crate features, WASM support and Custom RNGs see the ## Minimum Supported Rust Version -This crate requires Rust 1.42.0 or later. +This crate requires Rust 1.46.0 or later. ## Platform Support From fd15a6008113c78195873b5627757f749735b0bf Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 15:38:17 -0700 Subject: [PATCH 7/9] Bump MSRV to 1.57 so we can `panic!` in `const fn`. --- .clippy.toml | 2 +- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.clippy.toml b/.clippy.toml index eb66960a..5cccb362 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -msrv = "1.46" +msrv = "1.57" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2171c39e..f843c251 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: strategy: matrix: os: [ubuntu-22.04, windows-2022] - toolchain: [nightly, beta, stable, 1.46] + toolchain: [nightly, beta, stable, 1.57] # Only Test macOS on stable to reduce macOS CI jobs include: # x86_64-apple-darwin. diff --git a/CHANGELOG.md b/CHANGELOG.md index ba03e74f..e342e44e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Breaking Changes -- Update MSRV to 1.46 [#425], [#426] +- Update MSRV to 1.57 [#425], [#426] [#425]: https://github.com/rust-random/getrandom/pull/425 [#426]: https://github.com/rust-random/getrandom/pull/426 diff --git a/README.md b/README.md index 07d6cbbd..56af89dd 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ crate features, WASM support and Custom RNGs see the ## Minimum Supported Rust Version -This crate requires Rust 1.46.0 or later. +This crate requires Rust 1.57.0 or later. ## Platform Support From 07071c7eaf34e8afe55c46970c1f869fe55f5a0f Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 15:02:24 -0700 Subject: [PATCH 8/9] Improve the memory safety guarantees around `open_readonly`. Do more complete NUL termination checking, at compile-time. Remove the `unsafe` from the function as it is now memory-safe. --- src/use_file.rs | 9 +++--- src/util_cstr.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util_libc.rs | 15 +++++----- 3 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 src/util_cstr.rs diff --git a/src/use_file.rs b/src/use_file.rs index d2fe244f..0b7472e3 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -1,6 +1,6 @@ //! Implementations that just need to read from a file use crate::{ - util_libc::{open_readonly, sys_fill_exact}, + util_libc::{cstr, open_readonly, sys_fill_exact}, Error, }; use core::{ @@ -16,7 +16,7 @@ use core::{ /// - 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: &str = "/dev/urandom\0"; +const FILE_PATH: cstr::Ref = cstr::unwrap_const_from_bytes_with_nul(b"/dev/urandom\0"); const FD_UNINIT: usize = usize::max_value(); pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { @@ -57,7 +57,7 @@ fn get_rng_fd() -> Result { #[cfg(any(target_os = "android", target_os = "linux"))] wait_until_rng_ready()?; - let fd = unsafe { open_readonly(FILE_PATH)? }; + let fd = open_readonly(FILE_PATH)?; // The fd always fits in a usize without conflicting with FD_UNINIT. debug_assert!(fd >= 0 && (fd as usize) < FD_UNINIT); FD.store(fd as usize, Relaxed); @@ -68,8 +68,9 @@ fn get_rng_fd() -> Result { // Succeeds once /dev/urandom is safe to read from #[cfg(any(target_os = "android", target_os = "linux"))] fn wait_until_rng_ready() -> Result<(), Error> { + const DEV_RANDOM: cstr::Ref = cstr::unwrap_const_from_bytes_with_nul(b"/dev/random\0"); // Poll /dev/random to make sure it is ok to read from /dev/urandom. - let fd = unsafe { open_readonly("/dev/random\0")? }; + let fd = open_readonly(DEV_RANDOM)?; let mut pfd = libc::pollfd { fd, events: libc::POLLIN, diff --git a/src/util_cstr.rs b/src/util_cstr.rs new file mode 100644 index 00000000..6c70d912 --- /dev/null +++ b/src/util_cstr.rs @@ -0,0 +1,77 @@ +//! Work around lack of `core::ffi::CStr` prior to Rust 1.64, and the lack of +//! `const fn` support for `CStr` in later versions. + +// TODO(MSRV 1.64): Use `core::ffi::c_char`. +use libc::c_char; + +// TODO(MSRV 1.64): Replace with `&core::ffi::CStr`. +pub struct Ref(&'static [u8]); + +impl Ref { + #[inline(always)] + pub fn as_ptr(&self) -> *const c_char { + const _SAME_ALIGNMENT: () = + assert!(core::mem::align_of::() == core::mem::align_of::()); + const _SAME_SIZE: () = + assert!(core::mem::size_of::() == core::mem::size_of::()); + + // It is safe to cast a `*const u8` to a `const c_char` as they are the + // same size and alignment. + self.0.as_ptr().cast() + } + + // SAFETY: Same as `CStr::from_bytes_with_nul_unchecked`. + const unsafe fn from_bytes_with_nul_unchecked(value: &'static [u8]) -> Self { + Self(value) + } +} + +pub const fn unwrap_const_from_bytes_with_nul(value: &'static [u8]) -> Ref { + // XXX: We cannot use `unwrap_const` since `Ref`/`CStr` is not `Copy`. + match const_from_bytes_with_nul(value) { + Some(r) => r, + None => panic!("const_from_bytes_with_nul failed"), + } +} + +// TODO(MSRV 1.72): Replace with `CStr::from_bytes_with_nul`. +#[inline(always)] +const fn const_from_bytes_with_nul(value: &'static [u8]) -> Option { + const fn const_contains(mut value: &[u8], needle: &u8) -> bool { + while let [head, tail @ ..] = value { + if *head == *needle { + return true; + } + value = tail; + } + false + } + + // TODO(MSRV 1.69): Use `core::ffi::CStr::from_bytes_until_nul` + match value { + [before_nul @ .., 0] if !const_contains(before_nul, &0) => { + // SAFETY: + // * `value` is nul-terminated according to the slice pattern. + // * `value` doesn't contain any interior null, by the guard. + // TODO(MSRV 1.64): Use `CStr::from_bytes_with_nul_unchecked` + Some(unsafe { Ref::from_bytes_with_nul_unchecked(value) }) + } + _ => None, + } +} + +mod tests { + use super::const_from_bytes_with_nul; + + // Bad. + const _EMPTY_UNTERMINATED: () = assert!(const_from_bytes_with_nul(b"").is_none()); + const _EMPTY_DOUBLE_TERMINATED: () = assert!(const_from_bytes_with_nul(b"\0\0").is_none()); + const _DOUBLE_NUL: () = assert!(const_from_bytes_with_nul(b"\0\0").is_none()); + const _LEADINGL_NUL: () = assert!(const_from_bytes_with_nul(b"\0a\0").is_none()); + const _INTERNAL_NUL_UNTERMINATED: () = assert!(const_from_bytes_with_nul(b"\0a").is_none()); + + // Good. + const EMPTY_TERMINATED: () = assert!(const_from_bytes_with_nul(b"\0").is_some()); + const _NONEMPTY: () = assert!(const_from_bytes_with_nul(b"asdf\0").is_some()); + const _1_CHAR: () = assert!(const_from_bytes_with_nul(b"a\0").is_some()); +} diff --git a/src/util_libc.rs b/src/util_libc.rs index a447f424..520d5929 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -8,6 +8,9 @@ use core::{ }; use libc::c_void; +#[path = "util_cstr.rs"] +pub(crate) mod cstr; + cfg_if! { if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] { use libc::__errno as errno_location; @@ -133,15 +136,11 @@ impl Weak { } } -// SAFETY: path must be null terminated, FD must be manually closed. -pub unsafe fn open_readonly(path: &str) -> Result { - debug_assert_eq!(path.as_bytes().last(), Some(&0)); +// Memory leak hazard: The returned file descriptor must be manually closed to +// avoid a file descriptor leak. +pub fn open_readonly(path: cstr::Ref) -> Result { loop { - // XXX/FIXME: Unchecked UTF-8-to-c_char cast. - let fd = libc::open( - path.as_ptr().cast::(), - libc::O_RDONLY | libc::O_CLOEXEC, - ); + let fd = unsafe { libc::open(path.as_ptr(), libc::O_RDONLY | libc::O_CLOEXEC) }; if fd >= 0 { return Ok(fd); } From 7119c43875422e86104c39b40a5bfd38abb9da01 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 20 May 2024 15:06:08 -0700 Subject: [PATCH 9/9] Improve memory safety of `Weak::new()`. Do null terminator checking more completely and at compile time. --- src/netbsd.rs | 4 ++-- src/util_libc.rs | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/netbsd.rs b/src/netbsd.rs index 34c77837..b2f0ef3d 100644 --- a/src/netbsd.rs +++ b/src/netbsd.rs @@ -1,6 +1,6 @@ //! Implementation for NetBSD use crate::{ - util_libc::{sys_fill_exact, Weak}, + util_libc::{cstr, sys_fill_exact, Weak}, Error, }; use core::{ffi::c_void, mem::MaybeUninit, ptr}; @@ -29,7 +29,7 @@ type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { // getrandom(2) was introduced in NetBSD 10.0 - static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; + static GETRANDOM: Weak = Weak::new(cstr::unwrap_const_from_bytes_with_nul(b"getrandom\0")); if let Some(fptr) = GETRANDOM.ptr() { let func: GetRandomFn = unsafe { core::mem::transmute(fptr) }; return sys_fill_exact(dest, |buf| unsafe { diff --git a/src/util_libc.rs b/src/util_libc.rs index 520d5929..15f14b0b 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -85,7 +85,7 @@ pub fn sys_fill_exact( // https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84 // except that the caller must manually cast self.ptr() to a function pointer. pub struct Weak { - name: &'static str, + name: cstr::Ref, addr: AtomicPtr, } @@ -98,9 +98,8 @@ impl Weak { // TODO: Replace with core::ptr::invalid_mut(1) when that is stable. const UNINIT: *mut c_void = 1 as *mut c_void; - // Construct a binding to a C function with a given name. This function is - // unsafe because `name` _must_ be null terminated. - pub const unsafe fn new(name: &'static str) -> Self { + // Construct a binding to a C function with a given name. + pub const fn new(name: cstr::Ref) -> Self { Self { name, addr: AtomicPtr::new(Self::UNINIT), @@ -120,8 +119,7 @@ impl Weak { // the use of non-Relaxed operations is probably unnecessary. match self.addr.load(Ordering::Relaxed) { Self::UNINIT => { - // XXX/FIXME: Unchecked UTF-8-to-c_char cast. - let symbol = self.name.as_ptr().cast::(); + let symbol = self.name.as_ptr(); let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, symbol) }; // Synchronizes with the Acquire fence below self.addr.store(addr, Ordering::Release);