Skip to content

Commit

Permalink
Merge branch 'master' into linux_android/libc
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov authored Oct 9, 2024
2 parents 6d12dce + 387248f commit f5ef4bf
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 58 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ jobs:
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="linux_getrandom"
run: cargo test ${{ matrix.cargo_test_opts }} --target=${{ matrix.target }} --features=std
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_test_linux_fallback
run: cargo test --features=std
- env:
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="rdrand"
run: cargo test --features=std
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Breaking Changes
- Update MSRV to 1.60 [#472]
- Remove support of the `wasm32-wasi` target (use `wasm32-wasip1` or `wasm32-wasip2` instead) [#499]
- Remove `impl From<NonZeroU32> for Error` and `Error::code` method [#507]

### Changed
- Switch to `futex` on Linux and to `nanosleep`-based wait loop on other targets
in the `use_file` backend [#490]

### Added
- `wasm32-wasip1` and `wasm32-wasip2` support [#499]
- `Error::new_custom` method [#507]

[#472]: https://github.com/rust-random/getrandom/pull/472
[#490]: https://github.com/rust-random/getrandom/pull/490
[#499]: https://github.com/rust-random/getrandom/pull/499
[#507]: https://github.com/rust-random/getrandom/pull/507

## [0.2.15] - 2024-05-06
### Added
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ level = "warn"
check-cfg = [
'cfg(getrandom_backend, values("custom", "rdrand", "linux_getrandom", "wasm_js", "esp_idf"))',
'cfg(getrandom_browser_test)',
'cfg(getrandom_test_linux_fallback)',
]

[package.metadata.docs.rs]
Expand Down
10 changes: 3 additions & 7 deletions src/custom.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
//! An implementation which calls out to an externally defined function.
use crate::Error;
use core::{mem::MaybeUninit, num::NonZeroU32};
use core::mem::MaybeUninit;

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
extern "Rust" {
fn __getrandom_custom(dest: *mut u8, len: usize) -> u32;
}
let ret = unsafe { __getrandom_custom(dest.as_mut_ptr().cast(), dest.len()) };
match NonZeroU32::new(ret) {
None => Ok(()),
Some(code) => Err(Error::from(code)),
fn __getrandom_custom(dest: *mut u8, len: usize) -> Result<(), Error>;
}
unsafe { __getrandom_custom(dest.as_mut_ptr().cast(), dest.len()) }
}
59 changes: 26 additions & 33 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,38 @@ use core::{fmt, num::NonZeroU32};
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(NonZeroU32);

const fn internal_error(n: u16) -> Error {
// SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
let code = Error::INTERNAL_START + (n as u32);
Error(unsafe { NonZeroU32::new_unchecked(code) })
}

impl Error {
/// This target/platform is not supported by `getrandom`.
pub const UNSUPPORTED: Error = internal_error(0);
pub const UNSUPPORTED: Error = Self::new_internal(0);
/// The platform-specific `errno` returned a non-positive value.
pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
pub const ERRNO_NOT_POSITIVE: Error = Self::new_internal(1);
/// Encountered an unexpected situation which should not happen in practice.
pub const UNEXPECTED: Error = internal_error(2);
pub const UNEXPECTED: Error = Self::new_internal(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);
pub const IOS_SEC_RANDOM: Error = Self::new_internal(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);
pub const WINDOWS_RTL_GEN_RANDOM: Error = Self::new_internal(4);
/// RDRAND instruction failed due to a hardware issue.
pub const FAILED_RDRAND: Error = internal_error(5);
pub const FAILED_RDRAND: Error = Self::new_internal(5);
/// RDRAND instruction unsupported on this target.
pub const NO_RDRAND: Error = internal_error(6);
pub const NO_RDRAND: Error = Self::new_internal(6);
/// The environment does not support the Web Crypto API.
pub const WEB_CRYPTO: Error = internal_error(7);
pub const WEB_CRYPTO: Error = Self::new_internal(7);
/// Calling Web Crypto API `crypto.getRandomValues` failed.
pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
pub const WEB_GET_RANDOM_VALUES: Error = Self::new_internal(8);
/// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
pub const VXWORKS_RAND_SECURE: Error = Self::new_internal(11);
/// Node.js does not have the `crypto` CommonJS module.
pub const NODE_CRYPTO: Error = internal_error(12);
pub const NODE_CRYPTO: Error = Self::new_internal(12);
/// Calling Node.js function `crypto.randomFillSync` failed.
pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
pub const NODE_RANDOM_FILL_SYNC: Error = Self::new_internal(13);
/// Called from an ES module on Node.js. This is unsupported, see:
/// <https://docs.rs/getrandom#nodejs-es-module-support>.
pub const NODE_ES_MODULE: Error = internal_error(14);
pub const NODE_ES_MODULE: Error = Self::new_internal(14);
/// Calling Windows ProcessPrng failed.
pub const WINDOWS_PROCESS_PRNG: Error = internal_error(15);
pub const WINDOWS_PROCESS_PRNG: Error = Self::new_internal(15);

/// Codes below this point represent OS Errors (i.e. positive i32 values).
/// Codes at or above this point, but below [`Error::CUSTOM_START`] are
Expand Down Expand Up @@ -108,13 +102,18 @@ impl Error {
}
}

/// Extract the bare error code.
///
/// This code can either come from the underlying OS, or be a custom error.
/// Use [`Error::raw_os_error()`] to disambiguate.
#[inline]
pub const fn code(self) -> NonZeroU32 {
self.0
/// Creates a new instance of an `Error` from a particular custom error code.
pub const fn new_custom(n: u16) -> Error {
// SAFETY: code > 0 as CUSTOM_START > 0 and adding n won't overflow a u32.
let code = Error::CUSTOM_START + (n as u32);
Error(unsafe { NonZeroU32::new_unchecked(code) })
}

/// Creates a new instance of an `Error` from a particular internal error code.
const fn new_internal(n: u16) -> Error {
// SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
let code = Error::INTERNAL_START + (n as u32);
Error(unsafe { NonZeroU32::new_unchecked(code) })
}
}

Expand Down Expand Up @@ -153,12 +152,6 @@ impl fmt::Display for Error {
}
}

impl From<NonZeroU32> for Error {
fn from(code: NonZeroU32) -> Self {
Self(code)
}
}

fn internal_desc(error: Error) -> Option<&'static str> {
match error {
Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
Expand Down
10 changes: 7 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@
//! signature:
//!
//! ```
//! use getrandom::Error;
//!
//! #[no_mangle]
//! unsafe fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
//! unsafe extern "Rust" fn __getrandom_custom(dest: *mut u8, len: usize) -> Result<(), Error> {
//! todo!()
//! }
//! ```
Expand All @@ -126,9 +128,11 @@
//! it gets pulled nevertheless by one of your dependencies, then you can
//! use the following custom backend which always returns "unsupported" error:
//! ```
//! use getrandom::Error;
//!
//! #[no_mangle]
//! unsafe fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
//! getrandom::Error::UNSUPPORTED.code().get()
//! unsafe extern "Rust" fn __getrandom_custom(dest: *mut u8, len: usize) -> Result<(), Error> {
//! Err(Error::UNSUPPORTED)
//! }
//! ```
//!
Expand Down
4 changes: 3 additions & 1 deletion src/linux_android_with_fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ fn init() -> *mut c_void {
let fptr = unsafe { mem::transmute::<*mut c_void, GetRandomFn>(raw_ptr) };
// Check that `getrandom` syscall is supported by kernel
let res = unsafe { fptr(ptr::NonNull::dangling().as_ptr(), 0, 0) };
if res < 0 {
if cfg!(getrandom_test_linux_fallback) {
NOT_AVAILABLE
} else if res < 0 {
match util_libc::last_os_error().raw_os_error() {
Some(libc::ENOSYS) => NOT_AVAILABLE, // No kernel support
// The fallback on EPERM is intentionally not done on Android since this workaround
Expand Down
88 changes: 74 additions & 14 deletions tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use getrandom::getrandom;
use core::mem::MaybeUninit;
use getrandom::{getrandom, getrandom_uninit};

#[cfg(getrandom_browser_test)]
use wasm_bindgen_test::wasm_bindgen_test as test;
Expand All @@ -9,6 +10,8 @@ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
fn test_zero() {
// Test that APIs are happy with zero-length requests
getrandom(&mut [0u8; 0]).unwrap();
let res = getrandom_uninit(&mut []).unwrap();
assert!(res.is_empty());
}

// Return the number of bits in which s1 and s2 differ
Expand All @@ -20,34 +23,51 @@ fn num_diff_bits(s1: &[u8], s2: &[u8]) -> usize {
.sum()
}

// TODO: use `[const { MaybeUninit::uninit() }; N]` after MSRV is bumped to 1.79+
// or `MaybeUninit::uninit_array`
fn uninit_vec(n: usize) -> Vec<MaybeUninit<u8>> {
vec![MaybeUninit::uninit(); n]
}

// Tests the quality of calling getrandom on two large buffers
#[test]
fn test_diff() {
let mut v1 = [0u8; 1000];
const N: usize = 1000;
let mut v1 = [0u8; N];
let mut v2 = [0u8; N];
getrandom(&mut v1).unwrap();

let mut v2 = [0u8; 1000];
getrandom(&mut v2).unwrap();

let mut t1 = uninit_vec(N);
let mut t2 = uninit_vec(N);
let r1 = getrandom_uninit(&mut t1).unwrap();
let r2 = getrandom_uninit(&mut t2).unwrap();
assert_eq!(r1.len(), N);
assert_eq!(r2.len(), N);

// Between 3.5 and 4.5 bits per byte should differ. Probability of failure:
// ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500]
let d = num_diff_bits(&v1, &v2);
assert!(d > 3500);
assert!(d < 4500);
let d1 = num_diff_bits(&v1, &v2);
assert!(d1 > 3500);
assert!(d1 < 4500);
let d2 = num_diff_bits(r1, r2);
assert!(d2 > 3500);
assert!(d2 < 4500);
}

// Tests the quality of calling getrandom repeatedly on small buffers
#[test]
fn test_small() {
let mut buf1 = [0u8; 64];
let mut buf2 = [0u8; 64];
const N: usize = 64;
// For each buffer size, get at least 256 bytes and check that between
// 3 and 5 bits per byte differ. Probability of failure:
// ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256]
for size in 1..=64 {
for size in 1..=N {
let mut num_bytes = 0;
let mut diff_bits = 0;
while num_bytes < 256 {
let mut buf1 = [0u8; N];
let mut buf2 = [0u8; N];

let s1 = &mut buf1[..size];
let s2 = &mut buf2[..size];

Expand All @@ -62,12 +82,50 @@ fn test_small() {
}
}

// Tests the quality of calling getrandom repeatedly on small buffers
#[test]
fn test_small_uninit() {
const N: usize = 64;
// For each buffer size, get at least 256 bytes and check that between
// 3 and 5 bits per byte differ. Probability of failure:
// ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256]
for size in 1..=N {
let mut num_bytes = 0;
let mut diff_bits = 0;
while num_bytes < 256 {
let mut buf1 = uninit_vec(N);
let mut buf2 = uninit_vec(N);

let s1 = &mut buf1[..size];
let s2 = &mut buf2[..size];

let r1 = getrandom_uninit(s1).unwrap();
let r2 = getrandom_uninit(s2).unwrap();
assert_eq!(r1.len(), size);
assert_eq!(r2.len(), size);

num_bytes += size;
diff_bits += num_diff_bits(r1, r2);
}
assert!(diff_bits > 3 * num_bytes);
assert!(diff_bits < 5 * num_bytes);
}
}

#[test]
fn test_huge() {
let mut huge = [0u8; 100_000];
getrandom(&mut huge).unwrap();
}

#[test]
fn test_huge_uninit() {
const N: usize = 100_000;
let mut huge = uninit_vec(N);
let res = getrandom_uninit(&mut huge).unwrap();
assert_eq!(res.len(), N);
}

#[test]
#[cfg_attr(
target_arch = "wasm32",
Expand Down Expand Up @@ -102,6 +160,8 @@ fn test_multithreading() {

#[cfg(getrandom_backend = "custom")]
mod custom {
use getrandom::Error;

struct Xoshiro128PlusPlus {
s: [u32; 4],
}
Expand Down Expand Up @@ -146,13 +206,13 @@ mod custom {
//
// WARNING: this custom implementation is for testing purposes ONLY!
#[no_mangle]
unsafe fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
unsafe extern "Rust" fn __getrandom_custom(dest: *mut u8, len: usize) -> Result<(), Error> {
use std::time::{SystemTime, UNIX_EPOCH};

assert_ne!(len, 0);

if len == 142 {
return getrandom::Error::CUSTOM_START + 142;
return Err(Error::new_custom(142));
}

let dest_u32 = dest.cast::<u32>();
Expand All @@ -169,7 +229,7 @@ mod custom {
core::ptr::write_unaligned(dest.add(i), val as u8);
}
}
0
Ok(())
}

// Test that enabling the custom feature indeed uses the custom implementation
Expand Down

0 comments on commit f5ef4bf

Please sign in to comment.