From 3d16230b9b8c0ab611d64b1e5acc19c01c2f19b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 9 Oct 2024 17:59:43 +0300 Subject: [PATCH] Replace more casts with safer conversions --- src/error.rs | 18 +++++++----------- src/hermit.rs | 22 +++++++++++----------- src/js.rs | 12 ++++++++---- src/linux_android.rs | 12 ++++++++---- src/util_libc.rs | 5 ++++- src/windows7.rs | 3 ++- 6 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/error.rs b/src/error.rs index 1c158439..d0299267 100644 --- a/src/error.rs +++ b/src/error.rs @@ -88,18 +88,14 @@ impl Error { /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error #[inline] pub fn raw_os_error(self) -> Option { - if self.0.get() < Self::INTERNAL_START { - match () { - #[cfg(target_os = "solid_asp3")] - // On SOLID, negate the error code again to obtain the original - // error code. - () => Some(-(self.0.get() as i32)), - #[cfg(not(target_os = "solid_asp3"))] - () => Some(self.0.get() as i32), + i32::try_from(self.0.get()).ok().map(|errno| { + // On SOLID, negate the error code again to obtain the original error code. + if cfg!(target_os = "solid_asp3") { + -errno + } else { + errno } - } else { - None - } + }) } /// Creates a new instance of an `Error` from a particular custom error code. diff --git a/src/hermit.rs b/src/hermit.rs index c869372a..a1223c2b 100644 --- a/src/hermit.rs +++ b/src/hermit.rs @@ -9,18 +9,18 @@ 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().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..]; - } else { - let err = if res < 0 { - u32::try_from(res.unsigned_abs()) + 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)?; + } + 0 => Error::UNEXPECTED, + code => { + let err = u32::try_from(code.unsigned_abs()) .ok() - .map_or(Error::UNEXPECTED, Error::from_os_error) - } else { - Error::UNEXPECTED - }; - return Err(err); + .map_or(Error::UNEXPECTED, Error::from_os_error); + return Err(err); + } } } Ok(()) diff --git a/src/js.rs b/src/js.rs index bec31ee5..65f63a1a 100644 --- a/src/js.rs +++ b/src/js.rs @@ -9,7 +9,7 @@ use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; // Size of our temporary Uint8Array buffer used with WebCrypto methods // Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues -const WEB_CRYPTO_BUFFER_SIZE: usize = 256; +const WEB_CRYPTO_BUFFER_SIZE: u16 = 256; // Node.js's crypto.randomFillSync requires the size to be less than 2**31. const NODE_MAX_BUFFER_SIZE: usize = (1 << 31) - 1; @@ -50,10 +50,14 @@ pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> RngSource::Web(crypto, buf) => { // getRandomValues does not work with all types of WASM memory, // so we initially write to browser memory to avoid exceptions. - for chunk in dest.chunks_mut(WEB_CRYPTO_BUFFER_SIZE) { + for chunk in dest.chunks_mut(WEB_CRYPTO_BUFFER_SIZE.into()) { + let chunk_len: u32 = chunk + .len() + .try_into() + .expect("chunk length is bounded by WEB_CRYPTO_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 = buf.subarray(0, chunk.len() as u32); + let sub_buf = buf.subarray(0, chunk_len); if crypto.get_random_values(&sub_buf).is_err() { return Err(Error::WEB_GET_RANDOM_VALUES); @@ -95,7 +99,7 @@ fn getrandom_init() -> Result { }, }; - let buf = Uint8Array::new_with_length(WEB_CRYPTO_BUFFER_SIZE as u32); + let buf = Uint8Array::new_with_length(WEB_CRYPTO_BUFFER_SIZE.into()); Ok(RngSource::Web(crypto, buf)) } diff --git a/src/linux_android.rs b/src/linux_android.rs index 7c1fede4..401d3dfc 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -6,14 +6,18 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { util_libc::sys_fill_exact(dest, getrandom_syscall) } -// Also used by linux_android_with_fallback to check if the syscall is available. pub fn getrandom_syscall(buf: &mut [MaybeUninit]) -> libc::ssize_t { - unsafe { + let res: libc::c_long = unsafe { libc::syscall( libc::SYS_getrandom, buf.as_mut_ptr().cast::(), buf.len(), 0, - ) as libc::ssize_t - } + ) + }; + + const _: () = + assert!(core::mem::size_of::() == core::mem::size_of::()); + res.try_into() + .expect("c_long to ssize_t conversion is lossless") } diff --git a/src/util_libc.rs b/src/util_libc.rs index 7708bfcc..4b33b8cd 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -56,7 +56,10 @@ pub fn sys_fill_exact( while !buf.is_empty() { let res = sys_fill(buf); match res { - res if res > 0 => buf = buf.get_mut(res as usize..).ok_or(Error::UNEXPECTED)?, + res if res > 0 => { + let len = usize::try_from(res).map_err(|_| Error::UNEXPECTED)?; + buf = buf.get_mut(len..).ok_or(Error::UNEXPECTED)?; + } -1 => { let err = last_os_error(); // We should try again if the call was interrupted. diff --git a/src/windows7.rs b/src/windows7.rs index 6714f66b..667bbf26 100644 --- a/src/windows7.rs +++ b/src/windows7.rs @@ -27,7 +27,8 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> 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 ret = unsafe { RtlGenRandom(chunk.as_mut_ptr().cast::(), chunk.len() as u32) }; + 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::(), chunk_len) }; if ret != TRUE { return Err(Error::WINDOWS_RTL_GEN_RANDOM); }