diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e6534e9e..e3c9bbe6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -327,6 +327,17 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo build -Z build-std=${{ contains(matrix.features, 'std') && 'std' || 'core'}} --target=${{ matrix.target }} --features="${{ join(matrix.features, ',') }}" + build-no-atomics: + name: No Atomics Build + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: riscv32i-unknown-none-elf + - uses: Swatinem/rust-cache@v2 + - run: cargo build --features custom --target riscv32i-unknown-none-elf + clippy-fmt: name: Clippy + rustfmt runs-on: ubuntu-22.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b57e187..a283d380 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,34 @@ 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). +## [0.2.12] - 2024-01-09 +### Fixed +- Custom backend for targets without atomics [#385] + +### Changed +- Improve robustness of the Hermit backend and `sys_fill_exact` [#386] +- Raise minimum supported Apple OS versions to macOS 10.12 and iOS 10 [#388] + +### Added +- Document platform support policy [#387] + +[#385]: https://github.com/rust-random/getrandom/pull/385 +[#386]: https://github.com/rust-random/getrandom/pull/386 +[#387]: https://github.com/rust-random/getrandom/pull/387 +[#388]: https://github.com/rust-random/getrandom/pull/388 + +## [0.2.11] - 2023-11-08 +### Added +- GNU/Hurd support [#370] + +### Changed +- Renamed `__getrandom_internal` to `__GETRANDOM_INTERNAL` [#369] +- Updated link to Hermit docs [#374] + +[#369]: https://github.com/rust-random/getrandom/pull/369 +[#370]: https://github.com/rust-random/getrandom/pull/370 +[#374]: https://github.com/rust-random/getrandom/pull/374 + ## [0.2.10] - 2023-06-06 ### Added - Support for PS Vita (`armv7-sony-vita-newlibeabihf`) [#359] @@ -391,6 +419,8 @@ Publish initial implementation. ## [0.0.0] - 2019-01-19 Publish an empty template library. +[0.2.12]: https://github.com/rust-random/getrandom/compare/v0.2.11...v0.2.12 +[0.2.11]: https://github.com/rust-random/getrandom/compare/v0.2.10...v0.2.11 [0.2.10]: https://github.com/rust-random/getrandom/compare/v0.2.9...v0.2.10 [0.2.9]: https://github.com/rust-random/getrandom/compare/v0.2.8...v0.2.9 [0.2.8]: https://github.com/rust-random/getrandom/compare/v0.2.7...v0.2.8 diff --git a/Cargo.toml b/Cargo.toml index 0e9b09ac..21873552 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "getrandom" -version = "0.2.10" # Also update html_root_url in lib.rs when bumping this +version = "0.2.12" # Also update html_root_url in lib.rs when bumping this edition = "2018" authors = ["The Rand Project Developers"] license = "MIT OR Apache-2.0" diff --git a/LICENSE-MIT b/LICENSE-MIT index d93b5baf..8ca28a1a 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright 2018 Developers of the Rand project +Copyright (c) 2018-2024 The rust-random Project Developers Copyright (c) 2014 The Rust Project Developers Permission is hereby granted, free of charge, to any diff --git a/README.md b/README.md index 404b383b..b4b5a2b5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status]][GitHub Actions] [![Crate]][crates.io] [![Documentation]][docs.rs] [![Dependency Status]][deps.rs] [![Downloads]][crates.io] [![License]][LICENSE-MIT] [GitHub Actions]: https://github.com/rust-random/getrandom/actions?query=workflow:Tests+branch:master -[Build Status]: https://github.com/rust-random/getrandom/workflows/Tests/badge.svg?branch=master +[Build Status]: https://github.com/rust-random/getrandom/actions/workflows/tests.yml/badge.svg?branch=master [crates.io]: https://crates.io/crates/getrandom [Crate]: https://img.shields.io/crates/v/getrandom [docs.rs]: https://docs.rs/getrandom @@ -54,11 +54,28 @@ crate features, WASM support and Custom RNGs see the This crate requires Rust 1.36.0 or later. -# License +## Platform Support + +This crate generally supports the same operating system and platform versions that the Rust standard library does. +Additional targets may be supported using pluggable custom implementations. + +This means that as Rust drops support for old versions of operating systems (such as old Linux kernel versions, Android API levels, etc) +in stable releases, `getrandom` may create new patch releases (`0.N.x`) that remove support for outdated platform versions. + +## License The `getrandom` library is distributed under either of - * [Apache License, Version 2.0](LICENSE-APACHE) - * [MIT license](LICENSE-MIT) + * [Apache License, Version 2.0][LICENSE-APACHE] + * [MIT license][LICENSE-MIT] at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[LICENSE-APACHE]: https://github.com/rust-random/getrandom/blob/master/LICENSE-APACHE +[LICENSE-MIT]: https://github.com/rust-random/getrandom/blob/master/LICENSE-MIT diff --git a/src/3ds.rs b/src/3ds.rs index 87a32a1e..a5aae77d 100644 --- a/src/3ds.rs +++ b/src/3ds.rs @@ -1,11 +1,3 @@ -// Copyright 2021 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for Nintendo 3DS use crate::util_libc::sys_fill_exact; use crate::Error; diff --git a/src/apple-other.rs b/src/apple-other.rs index 8f904859..167d8cf0 100644 --- a/src/apple-other.rs +++ b/src/apple-other.rs @@ -1,24 +1,21 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for iOS +//! Implementation for iOS, tvOS, and watchOS where `getentropy` is unavailable. use crate::Error; -use core::{ffi::c_void, mem::MaybeUninit, ptr::null}; +use core::{ffi::c_void, mem::MaybeUninit}; -#[link(name = "Security", kind = "framework")] +// libsystem contains the libc of Darwin, and every binary ends up linked against it either way. This +// makes it a more lightweight choice compared to `Security.framework`. extern "C" { - fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> i32; + // This RNG uses a thread-local CSPRNG to provide data, which is seeded by the operating system's root CSPRNG. + // Its the best option after `getentropy` on modern Darwin-based platforms that also avoids the + // high startup costs and linking of Security.framework. + // + // While its just an implementation detail, `Security.framework` just calls into this anyway. + fn CCRandomGenerateBytes(bytes: *mut c_void, size: usize) -> i32; } pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - // Apple's documentation guarantees kSecRandomDefault is a synonym for NULL. - let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr() as *mut u8) }; - // errSecSuccess (from SecBase.h) is always zero. + let ret = unsafe { CCRandomGenerateBytes(dest.as_mut_ptr() as *mut c_void, dest.len()) }; + // kCCSuccess (from CommonCryptoError.h) is always zero. if ret != 0 { Err(Error::IOS_SEC_RANDOM) } else { diff --git a/src/bsd_arandom.rs b/src/bsd_arandom.rs index 5314c48f..6e133d89 100644 --- a/src/bsd_arandom.rs +++ b/src/bsd_arandom.rs @@ -1,11 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for FreeBSD and NetBSD use crate::{ util_libc::{sys_fill_exact, Weak}, diff --git a/src/custom.rs b/src/custom.rs index 222477aa..79be7fc2 100644 --- a/src/custom.rs +++ b/src/custom.rs @@ -1,11 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! An implementation which calls out to an externally defined function. use crate::{util::uninit_slice_fill_zero, Error}; use core::{mem::MaybeUninit, num::NonZeroU32}; diff --git a/src/dragonfly.rs b/src/dragonfly.rs index d3ef00aa..ac4794cd 100644 --- a/src/dragonfly.rs +++ b/src/dragonfly.rs @@ -1,11 +1,3 @@ -// Copyright 2021 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for DragonFly BSD use crate::{ use_file, diff --git a/src/error.rs b/src/error.rs index ab39a3c3..13c81c7a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. use core::{fmt, num::NonZeroU32}; /// A small and `no_std` compatible error type @@ -35,7 +28,11 @@ impl Error { pub const UNSUPPORTED: Error = internal_error(0); /// The platform-specific `errno` returned a non-positive value. pub const ERRNO_NOT_POSITIVE: Error = internal_error(1); - /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed. + /// Encountered an unexpected situation which should not happen in practice. + pub const UNEXPECTED: Error = internal_error(2); + /// Call to [`CCRandomGenerateBytes`](https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html) failed + /// on iOS, tvOS, or waatchOS. + // TODO: Update this constant name in the next breaking release. pub const IOS_SEC_RANDOM: Error = internal_error(3); /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4); @@ -164,6 +161,7 @@ fn internal_desc(error: Error) -> Option<&'static str> { match error { Error::UNSUPPORTED => Some("getrandom: this target is not supported"), Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), + Error::UNEXPECTED => Some("unexpected situation"), Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"), Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"), Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), diff --git a/src/error_impls.rs b/src/error_impls.rs index 5cb88405..7f176cc6 100644 --- a/src/error_impls.rs +++ b/src/error_impls.rs @@ -1,10 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. extern crate std; use crate::Error; diff --git a/src/espidf.rs b/src/espidf.rs index d074dc4c..7da5ca88 100644 --- a/src/espidf.rs +++ b/src/espidf.rs @@ -1,11 +1,3 @@ -// Copyright 2021 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for ESP-IDF use crate::Error; use core::{ffi::c_void, mem::MaybeUninit}; diff --git a/src/fuchsia.rs b/src/fuchsia.rs index 5a135f34..11970685 100644 --- a/src/fuchsia.rs +++ b/src/fuchsia.rs @@ -1,11 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for Fuchsia Zircon use crate::Error; use core::mem::MaybeUninit; diff --git a/src/hermit.rs b/src/hermit.rs index 570b03d9..c4f61941 100644 --- a/src/hermit.rs +++ b/src/hermit.rs @@ -1,5 +1,11 @@ +//! Implementation for Hermit use crate::Error; -use core::{cmp::min, mem::MaybeUninit, num::NonZeroU32}; +use core::{mem::MaybeUninit, num::NonZeroU32}; + +/// Minimum return value which we should get from syscalls in practice, +/// because Hermit uses positive `i32`s for error codes: +/// https://github.com/hermitcore/libhermit-rs/blob/main/src/errno.rs +const MIN_RET_CODE: isize = -(i32::MAX as isize); extern "C" { fn sys_read_entropy(buffer: *mut u8, length: usize, flags: u32) -> isize; @@ -8,14 +14,16 @@ 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) }; - if res < 0 { - // SAFETY: all Hermit error codes use i32 under the hood: - // https://github.com/hermitcore/libhermit-rs/blob/master/src/errno.rs - let code = unsafe { NonZeroU32::new_unchecked((-res) as u32) }; - return Err(code.into()); + // 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 = match res { + MIN_RET_CODE..=-1 => NonZeroU32::new(-res as u32).unwrap().into(), + _ => Error::UNEXPECTED, + }; + return Err(err); } - let len = min(res as usize, dest.len()); - dest = &mut dest[len..]; } Ok(()) } diff --git a/src/hurd.rs b/src/hurd.rs index 842b9bc4..472a7d86 100644 --- a/src/hurd.rs +++ b/src/hurd.rs @@ -1,11 +1,3 @@ -// Copyright 2021 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for GNU/Hurd use crate::util_libc::sys_fill_exact; use crate::Error; diff --git a/src/js.rs b/src/js.rs index d0312822..e5428f50 100644 --- a/src/js.rs +++ b/src/js.rs @@ -1,10 +1,4 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +//! Implementation for WASM based on Web and Node.js use crate::Error; extern crate std; diff --git a/src/lazy.rs b/src/lazy.rs new file mode 100644 index 00000000..100ce1ea --- /dev/null +++ b/src/lazy.rs @@ -0,0 +1,56 @@ +use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + +// This structure represents a lazily initialized static usize value. Useful +// when it is preferable to just rerun initialization instead of locking. +// unsync_init will invoke an init() function until it succeeds, then return the +// cached value for future calls. +// +// unsync_init supports init() "failing". If the init() method returns UNINIT, +// that value will be returned as normal, but will not be cached. +// +// Users should only depend on the _value_ returned by init() functions. +// Specifically, for the following init() function: +// fn init() -> usize { +// a(); +// let v = b(); +// c(); +// v +// } +// the effects of c() or writes to shared memory will not necessarily be +// observed and additional synchronization methods may be needed. +pub(crate) struct LazyUsize(AtomicUsize); + +impl LazyUsize { + pub const fn new() -> Self { + Self(AtomicUsize::new(Self::UNINIT)) + } + + // The initialization is not completed. + pub const UNINIT: usize = usize::max_value(); + + // Runs the init() function at most once, returning the value of some run of + // init(). Multiple callers can run their init() functions in parallel. + // init() should always return the same value, if it succeeds. + pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize { + // Relaxed ordering is fine, as we only have a single atomic variable. + let mut val = self.0.load(Relaxed); + if val == Self::UNINIT { + val = init(); + self.0.store(val, Relaxed); + } + val + } +} + +// Identical to LazyUsize except with bool instead of usize. +pub(crate) struct LazyBool(LazyUsize); + +impl LazyBool { + pub const fn new() -> Self { + Self(LazyUsize::new()) + } + + pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool { + self.0.unsync_init(|| init() as usize) != 0 + } +} diff --git a/src/lib.rs b/src/lib.rs index 07d0ba3d..da733a3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,3 @@ -// Copyright 2019 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Interface to the operating system's random number generator. //! //! # Supported targets @@ -14,8 +6,8 @@ //! | ----------------- | ------------------ | -------------- //! | Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` //! | Windows | `*‑windows‑*` | [`BCryptGenRandom`] -//! | macOS | `*‑apple‑darwin` | [`getentropy`][3] if available, otherwise [`/dev/urandom`][4] (identical to `/dev/random`) -//! | iOS, tvOS, watchOS | `*‑apple‑ios`, `*-apple-tvos`, `*-apple-watchos` | [`SecRandomCopyBytes`] +//! | macOS | `*‑apple‑darwin` | [`getentropy`][3] +//! | iOS, tvOS, watchOS | `*‑apple‑ios`, `*-apple-tvos`, `*-apple-watchos` | [`CCRandomGenerateBytes`] //! | FreeBSD | `*‑freebsd` | [`getrandom`][5] if available, otherwise [`kern.arandom`][6] //! | OpenBSD | `*‑openbsd` | [`getentropy`][7] //! | NetBSD | `*‑netbsd` | [`getrandom`][16] if available, otherwise [`kern.arandom`][8] @@ -106,6 +98,13 @@ //! ``` //! This crate will then use the provided `webcrypto` implementation. //! +//! ### Platform Support +//! This crate generally supports the same operating system and platform versions that the Rust standard library does. +//! Additional targets may be supported using pluggable custom implementations. +//! +//! This means that as Rust drops support for old versions of operating systems (such as old Linux kernel versions, Android API levels, etc) +//! in stable releases, `getrandom` may create new patch releases (`0.N.x`) that remove support for outdated platform versions. +//! //! ### Custom implementations //! //! The [`register_custom_getrandom!`] macro allows a user to mark their own @@ -172,7 +171,7 @@ //! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom //! [`Crypto.getRandomValues`]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues //! [`RDRAND`]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide -//! [`SecRandomCopyBytes`]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc +//! [`CCRandomGenerateBytes`]: https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html //! [`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw //! [`crypto.randomFillSync`]: https://nodejs.org/api/crypto.html#cryptorandomfillsyncbuffer-offset-size //! [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t @@ -187,7 +186,7 @@ #![doc( html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", - html_root_url = "https://docs.rs/getrandom/0.2.10" + html_root_url = "https://docs.rs/getrandom/0.2.12" )] #![no_std] #![warn(rust_2018_idioms, unused_lifetimes, missing_docs)] @@ -227,6 +226,7 @@ cfg_if! { } else if #[cfg(any(target_os = "android", target_os = "linux"))] { mod util_libc; mod use_file; + mod lazy; #[path = "linux_android.rs"] mod imp; } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { mod util_libc; @@ -245,7 +245,6 @@ cfg_if! { #[path = "apple-other.rs"] mod imp; } else if #[cfg(target_os = "macos")] { mod util_libc; - mod use_file; #[path = "macos.rs"] mod imp; } else if #[cfg(target_os = "openbsd")] { mod util_libc; @@ -275,9 +274,11 @@ cfg_if! { mod util_libc; #[path = "emscripten.rs"] mod imp; } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] { + mod lazy; #[path = "rdrand.rs"] mod imp; } else if #[cfg(all(feature = "rdrand", any(target_arch = "x86_64", target_arch = "x86")))] { + mod lazy; #[path = "rdrand.rs"] mod imp; } else if #[cfg(all(feature = "js", any(target_arch = "wasm32", target_arch = "wasm64"), diff --git a/src/linux_android.rs b/src/linux_android.rs index e81f1e15..2517159e 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -1,14 +1,6 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for Linux / Android use crate::{ - util::LazyBool, + lazy::LazyBool, util_libc::{last_os_error, sys_fill_exact}, {use_file, Error}, }; diff --git a/src/macos.rs b/src/macos.rs index 312f9b27..44af76b0 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -1,36 +1,18 @@ -// Copyright 2019 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for macOS -use crate::{ - use_file, - util_libc::{last_os_error, Weak}, - Error, -}; -use core::mem::{self, MaybeUninit}; +use crate::{util_libc::last_os_error, Error}; +use core::mem::MaybeUninit; -type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; +extern "C" { + // Supported as of macOS 10.12+. + fn getentropy(buf: *mut u8, size: libc::size_t) -> libc::c_int; +} pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> 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() as *mut u8, chunk.len()) }; - if ret != 0 { - return Err(last_os_error()); - } + for chunk in dest.chunks_mut(256) { + let ret = unsafe { getentropy(chunk.as_mut_ptr() as *mut u8, chunk.len()) }; + if ret != 0 { + return Err(last_os_error()); } - 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) } + Ok(()) } diff --git a/src/openbsd.rs b/src/openbsd.rs index 7a76f61d..f4d64daf 100644 --- a/src/openbsd.rs +++ b/src/openbsd.rs @@ -1,11 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for OpenBSD use crate::{util_libc::last_os_error, Error}; use core::mem::MaybeUninit; diff --git a/src/rdrand.rs b/src/rdrand.rs index 69f6a5d1..f527c8c6 100644 --- a/src/rdrand.rs +++ b/src/rdrand.rs @@ -1,14 +1,5 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -use crate::{ - util::{slice_as_uninit, LazyBool}, - Error, -}; +//! RDRAND backend for x86(-64) targets +use crate::{lazy::LazyBool, util::slice_as_uninit, Error}; use core::mem::{size_of, MaybeUninit}; cfg_if! { diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs index 501c610d..fbc23943 100644 --- a/src/solaris_illumos.rs +++ b/src/solaris_illumos.rs @@ -1,11 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for the Solaris family //! //! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A. diff --git a/src/solid.rs b/src/solid.rs index aeccc4e2..cae8caf6 100644 --- a/src/solid.rs +++ b/src/solid.rs @@ -1,11 +1,3 @@ -// Copyright 2021 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for SOLID use crate::Error; use core::{mem::MaybeUninit, num::NonZeroU32}; diff --git a/src/use_file.rs b/src/use_file.rs index a6ef0d23..333325b5 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -1,14 +1,5 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementations that just need to read from a file use crate::{ - util::LazyUsize, util_libc::{open_readonly, sys_fill_exact}, Error, }; @@ -21,7 +12,7 @@ use core::{ // We prefer using /dev/urandom and only use /dev/random if the OS // documentation indicates that /dev/urandom is insecure. // On Solaris/Illumos, see src/solaris_illumos.rs -// On Dragonfly, Haiku, macOS, and QNX Neutrino the devices are identical. +// On Dragonfly, Haiku, and QNX Neutrino the devices are identical. #[cfg(any(target_os = "solaris", target_os = "illumos"))] const FILE_PATH: &str = "/dev/random\0"; #[cfg(any( @@ -31,10 +22,10 @@ const FILE_PATH: &str = "/dev/random\0"; target_os = "redox", target_os = "dragonfly", target_os = "haiku", - target_os = "macos", target_os = "nto", ))] const FILE_PATH: &str = "/dev/urandom\0"; +const FD_UNINIT: usize = usize::max_value(); pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { let fd = get_rng_fd()?; @@ -47,10 +38,10 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { // bytes. The file will be opened exactly once. All subsequent calls will // return the same file descriptor. This file descriptor is never closed. fn get_rng_fd() -> Result { - static FD: AtomicUsize = AtomicUsize::new(LazyUsize::UNINIT); + static FD: AtomicUsize = AtomicUsize::new(FD_UNINIT); fn get_fd() -> Option { match FD.load(Relaxed) { - LazyUsize::UNINIT => None, + FD_UNINIT => None, val => Some(val as libc::c_int), } } @@ -75,8 +66,8 @@ fn get_rng_fd() -> Result { wait_until_rng_ready()?; let fd = unsafe { open_readonly(FILE_PATH)? }; - // The fd always fits in a usize without conflicting with UNINIT. - debug_assert!(fd >= 0 && (fd as usize) < LazyUsize::UNINIT); + // 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); Ok(fd) diff --git a/src/util.rs b/src/util.rs index 3162afad..1c4e70ba 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,71 +1,5 @@ -// Copyright 2019 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. #![allow(dead_code)] -use core::{ - mem::MaybeUninit, - ptr, - sync::atomic::{AtomicUsize, Ordering::Relaxed}, -}; - -// This structure represents a lazily initialized static usize value. Useful -// when it is preferable to just rerun initialization instead of locking. -// Both unsync_init and sync_init will invoke an init() function until it -// succeeds, then return the cached value for future calls. -// -// Both methods support init() "failing". If the init() method returns UNINIT, -// that value will be returned as normal, but will not be cached. -// -// Users should only depend on the _value_ returned by init() functions. -// Specifically, for the following init() function: -// fn init() -> usize { -// a(); -// let v = b(); -// c(); -// v -// } -// the effects of c() or writes to shared memory will not necessarily be -// observed and additional synchronization methods with be needed. -pub struct LazyUsize(AtomicUsize); - -impl LazyUsize { - pub const fn new() -> Self { - Self(AtomicUsize::new(Self::UNINIT)) - } - - // The initialization is not completed. - pub const UNINIT: usize = usize::max_value(); - - // Runs the init() function at least once, returning the value of some run - // of init(). Multiple callers can run their init() functions in parallel. - // init() should always return the same value, if it succeeds. - pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize { - // Relaxed ordering is fine, as we only have a single atomic variable. - let mut val = self.0.load(Relaxed); - if val == Self::UNINIT { - val = init(); - self.0.store(val, Relaxed); - } - val - } -} - -// Identical to LazyUsize except with bool instead of usize. -pub struct LazyBool(LazyUsize); - -impl LazyBool { - pub const fn new() -> Self { - Self(LazyUsize::new()) - } - - pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool { - self.0.unsync_init(|| init() as usize) != 0 - } -} +use core::{mem::MaybeUninit, ptr}; /// Polyfill for `maybe_uninit_slice` feature's /// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have diff --git a/src/util_libc.rs b/src/util_libc.rs index 99bee382..0b792c35 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -1,14 +1,6 @@ -// Copyright 2019 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. #![allow(dead_code)] use crate::Error; use core::{ - cmp::min, mem::MaybeUninit, num::NonZeroU32, ptr::NonNull, @@ -70,17 +62,19 @@ pub fn sys_fill_exact( ) -> Result<(), Error> { while !buf.is_empty() { let res = sys_fill(buf); - if res < 0 { - let err = last_os_error(); - // We should try again if the call was interrupted. - if err.raw_os_error() != Some(libc::EINTR) { - return Err(err); + match res { + res if res > 0 => buf = buf.get_mut(res as usize..).ok_or(Error::UNEXPECTED)?, + -1 => { + let err = last_os_error(); + // We should try again if the call was interrupted. + if err.raw_os_error() != Some(libc::EINTR) { + return Err(err); + } } - } else { - // We don't check for EOF (ret = 0) as the data we are reading + // Negative return codes not equal to -1 should be impossible. + // EOF (ret = 0) should be impossible, as the data we are reading // should be an infinite stream of random bytes. - let len = min(res as usize, buf.len()); - buf = &mut buf[len..]; + _ => return Err(Error::UNEXPECTED), } } Ok(()) diff --git a/src/vita.rs b/src/vita.rs index 4f19b9cb..20a98782 100644 --- a/src/vita.rs +++ b/src/vita.rs @@ -1,11 +1,3 @@ -// Copyright 2021 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for PS Vita use crate::{util_libc::last_os_error, Error}; use core::mem::MaybeUninit; diff --git a/src/vxworks.rs b/src/vxworks.rs index 9b2090fb..7ca9d6bf 100644 --- a/src/vxworks.rs +++ b/src/vxworks.rs @@ -1,11 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for VxWorks use crate::{util_libc::last_os_error, Error}; use core::{ diff --git a/src/wasi.rs b/src/wasi.rs index 9276ee74..d6c8a912 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -1,11 +1,3 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! Implementation for WASI use crate::Error; use core::{ diff --git a/src/windows.rs b/src/windows.rs index 92d70429..2d1c4835 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,11 +1,4 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - +//! Implementation for Windows use crate::Error; use core::{ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr}; diff --git a/tests/rdrand.rs b/tests/rdrand.rs index 25678683..a355c31e 100644 --- a/tests/rdrand.rs +++ b/tests/rdrand.rs @@ -6,6 +6,8 @@ use getrandom::Error; #[macro_use] extern crate cfg_if; +#[path = "../src/lazy.rs"] +mod lazy; #[path = "../src/rdrand.rs"] mod rdrand; #[path = "../src/util.rs"]