Skip to content

Commit

Permalink
Test getrandom_uninit() with initially-uninitialized buffer.
Browse files Browse the repository at this point in the history
None of the tests are testing `getrandom_uninit()` directly, but
instead it is marked as covered because `getrandom()` forwards to
it. However, this means we're never testing an uninitialized input
to `getrandom_uninit()`. Fix that.
  • Loading branch information
briansmith committed Jun 6, 2024
1 parent 05cdf6f commit 1175729
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 21 deletions.
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;

0 comments on commit 1175729

Please sign in to comment.