Skip to content

Test getrandom_uninit() with initially-uninitialized buffer. #462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 59 additions & 17 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
use super::getrandom_impl;
use super::{getrandom_impl, getrandom_uninit_impl};
use core::mem::MaybeUninit;
#[cfg(not(feature = "custom"))]
use getrandom::Error;

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use wasm_bindgen_test::wasm_bindgen_test as test;

#[cfg(feature = "test-in-browser")]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

#[cfg(not(feature = "custom"))]
fn wrapped_getrandom(dest: &mut [u8]) -> Result<&mut [u8], Error> {
getrandom_impl(dest).map(|()| dest)
}

// Test that APIs are happy with zero-length requests
#[test]
fn test_zero() {
// Test that APIs are happy with zero-length requests
getrandom_impl(&mut [0u8; 0]).unwrap();
getrandom_impl(&mut []).unwrap();
}
#[test]
fn test_zero_uninit() {
getrandom_uninit_impl(&mut []).unwrap();
}

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

// Tests the quality of calling getrandom on two large buffers
#[test]

#[cfg(not(feature = "custom"))]
fn test_diff() {
let mut v1 = [0u8; 1000];
getrandom_impl(&mut v1).unwrap();
fn test_diff_large<T: Copy>(initial: T, f: impl Fn(&mut [T]) -> Result<&mut [u8], Error>) {
let mut v1 = [initial; 1000];
let r1 = f(&mut v1).unwrap();

let mut v2 = [0u8; 1000];
getrandom_impl(&mut v2).unwrap();
let mut v2 = [initial; 1000];
let r2 = f(&mut v2).unwrap();

// 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);
let d = num_diff_bits(r1, r2);
assert!(d > 3500);
assert!(d < 4500);
}

// Tests the quality of calling getrandom repeatedly on small buffers
#[cfg(not(feature = "custom"))]
#[test]
fn test_large() {
test_diff_large(0u8, wrapped_getrandom);
}

#[cfg(not(feature = "custom"))]
fn test_small() {
#[test]
fn test_large_uninit() {
test_diff_large(MaybeUninit::uninit(), getrandom_uninit_impl);
}

// Tests the quality of calling getrandom repeatedly on small buffers

#[cfg(not(feature = "custom"))]
fn test_diff_small<T: Copy>(initial: T, f: impl Fn(&mut [T]) -> Result<&mut [u8], Error>) {
// 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 {
let mut num_bytes = 0;
let mut diff_bits = 0;
while num_bytes < 256 {
let mut s1 = vec![0u8; size];
getrandom_impl(&mut s1).unwrap();
let mut s2 = vec![0u8; size];
getrandom_impl(&mut s2).unwrap();
let mut s1 = vec![initial; size];
let r1 = f(&mut s1).unwrap();
let mut s2 = vec![initial; size];
let r2 = f(&mut s2).unwrap();

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

#[cfg(not(feature = "custom"))]
#[test]
fn test_small() {
test_diff_small(0u8, wrapped_getrandom);
}

#[cfg(not(feature = "custom"))]
#[test]
fn test_small_unnit() {
test_diff_small(MaybeUninit::uninit(), getrandom_uninit_impl);
}

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

#[test]
fn test_huge_uninit() {
let mut huge = [MaybeUninit::uninit(); 100_000];
getrandom_uninit_impl(&mut huge).unwrap();
}

// On WASM, the thread API always fails/panics
#[cfg(not(target_arch = "wasm32"))]
#[test]
Expand Down
2 changes: 1 addition & 1 deletion tests/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn super_insecure_rng(buf: &mut [u8]) -> Result<(), Error> {

register_custom_getrandom!(super_insecure_rng);

use getrandom::getrandom as getrandom_impl;
use getrandom::{getrandom as getrandom_impl, getrandom_uninit as getrandom_uninit_impl};
mod common;

#[test]
Expand Down
2 changes: 1 addition & 1 deletion tests/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
)))]

// Use the normal getrandom implementation on this architecture.
use getrandom::getrandom as getrandom_impl;
use getrandom::{getrandom as getrandom_impl, getrandom_uninit as getrandom_uninit_impl};
mod common;
10 changes: 8 additions & 2 deletions tests/rdrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

// rdrand.rs expects to be part of the getrandom main crate, so we need these
// additional imports to get rdrand.rs to compile.
use core::mem::MaybeUninit;
use getrandom::Error;
#[macro_use]
extern crate cfg_if;
Expand All @@ -13,10 +14,15 @@ mod rdrand;
#[path = "../src/util.rs"]
mod util;

// The rdrand implementation has the signature of getrandom_uninit(), but our
// tests expect getrandom_impl() to have the signature of getrandom().
use crate::util::slice_assume_init_mut;

fn getrandom_impl(dest: &mut [u8]) -> Result<(), Error> {
rdrand::getrandom_inner(unsafe { util::slice_as_uninit_mut(dest) })?;
Ok(())
}
fn getrandom_uninit_impl(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> {
rdrand::getrandom_inner(dest)?;
Ok(unsafe { slice_assume_init_mut(dest) })
}

mod common;
Loading