Skip to content

Commit 47a59dd

Browse files
briansmithjosephlr
andauthored
Add getrandom_uninit_slice(dest: &mut [MaybeUninit<u8>]) -> .... (#291)
* Add `getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> ...`. Add a public API for filling an `&mut [MaybeUninit<u8>]`. This will primarily serve as the building block for more typeful APIs for constructing random arrays. Increase the MSRV to 1.36, as `MaybeUninit` was added in that release. Fixes #226. * Revert testing changes Signed-off-by: Joe Richey <joerichey@google.com> * Allow rdrand tests to work with new implementation Signed-off-by: Joe Richey <joerichey@google.com> * Add Additional benchmarks and buffer size Signed-off-by: Joe Richey <joerichey@google.com> * Use pointer casts instead of transmute Signed-off-by: Joe Richey <joerichey@google.com> * Avoid initializing the buffer in `getrandom_uninit` benchmarks. * Benchmarks: Consume the result in `black_box`. Signed-off-by: Joe Richey <joerichey@google.com> Co-authored-by: Joe Richey <joerichey@google.com>
1 parent 5c1bb00 commit 47a59dd

26 files changed

+211
-127
lines changed

.github/workflows/tests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
strategy:
4545
matrix:
4646
os: [ubuntu-latest, windows-latest]
47-
toolchain: [nightly, beta, stable, 1.34]
47+
toolchain: [nightly, beta, stable, 1.36]
4848
# Only Test macOS on stable to reduce macOS CI jobs
4949
include:
5050
- os: macos-latest

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## Unreleased
8+
### Added
9+
- `getrandom_uninit` [#291]
10+
11+
### Breaking Changes
12+
- Update MSRV to 1.36 [#291]
13+
14+
[#291]: https://github.com/rust-random/getrandom/pull/291
15+
716
## [0.2.8] - 2022-10-20
817
### Changed
918
- The [Web Cryptography API] will now be preferred on `wasm32-unknown-unknown`

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ crate features, WASM support and Custom RNGs see the
5252

5353
## Minimum Supported Rust Version
5454

55-
This crate requires Rust 1.34.0 or later.
55+
This crate requires Rust 1.36.0 or later.
5656

5757
# License
5858

benches/mod.rs

+41-71
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,64 @@
11
#![feature(test)]
2-
extern crate test;
3-
4-
use std::{
5-
alloc::{alloc_zeroed, dealloc, Layout},
6-
ptr::NonNull,
7-
};
8-
9-
// AlignedBuffer is like a Box<[u8; N]> except that it is always N-byte aligned
10-
struct AlignedBuffer<const N: usize>(NonNull<[u8; N]>);
2+
#![feature(maybe_uninit_as_bytes)]
113

12-
impl<const N: usize> AlignedBuffer<N> {
13-
fn layout() -> Layout {
14-
Layout::from_size_align(N, N).unwrap()
15-
}
16-
17-
fn new() -> Self {
18-
let p = unsafe { alloc_zeroed(Self::layout()) } as *mut [u8; N];
19-
Self(NonNull::new(p).unwrap())
20-
}
21-
22-
fn buf(&mut self) -> &mut [u8; N] {
23-
unsafe { self.0.as_mut() }
24-
}
25-
}
4+
extern crate test;
265

27-
impl<const N: usize> Drop for AlignedBuffer<N> {
28-
fn drop(&mut self) {
29-
unsafe { dealloc(self.0.as_ptr() as *mut u8, Self::layout()) }
30-
}
31-
}
6+
use std::mem::MaybeUninit;
327

338
// Used to benchmark the throughput of getrandom in an optimal scenario.
349
// The buffer is hot, and does not require initialization.
3510
#[inline(always)]
36-
fn bench<const N: usize>(b: &mut test::Bencher) {
37-
let mut ab = AlignedBuffer::<N>::new();
38-
let buf = ab.buf();
11+
fn bench_getrandom<const N: usize>(b: &mut test::Bencher) {
12+
b.bytes = N as u64;
3913
b.iter(|| {
14+
let mut buf = [0u8; N];
4015
getrandom::getrandom(&mut buf[..]).unwrap();
41-
test::black_box(&buf);
16+
test::black_box(buf);
4217
});
43-
b.bytes = N as u64;
4418
}
4519

4620
// Used to benchmark the throughput of getrandom is a slightly less optimal
4721
// scenario. The buffer is still hot, but requires initialization.
4822
#[inline(always)]
49-
fn bench_with_init<const N: usize>(b: &mut test::Bencher) {
50-
let mut ab = AlignedBuffer::<N>::new();
51-
let buf = ab.buf();
23+
fn bench_getrandom_uninit<const N: usize>(b: &mut test::Bencher) {
24+
b.bytes = N as u64;
5225
b.iter(|| {
53-
for byte in buf.iter_mut() {
54-
*byte = 0;
55-
}
56-
getrandom::getrandom(&mut buf[..]).unwrap();
57-
test::black_box(&buf);
26+
let mut buf: MaybeUninit<[u8; N]> = MaybeUninit::uninit();
27+
let _ = getrandom::getrandom_uninit(buf.as_bytes_mut()).unwrap();
28+
let buf: [u8; N] = unsafe { buf.assume_init() };
29+
test::black_box(buf)
5830
});
59-
b.bytes = N as u64;
6031
}
6132

62-
// 32 bytes (256-bit) is the seed sized used for rand::thread_rng
63-
const SEED: usize = 32;
64-
// Common size of a page, 4 KiB
65-
const PAGE: usize = 4096;
66-
// Large buffer to get asymptotic performance, 2 MiB
67-
const LARGE: usize = 1 << 21;
33+
macro_rules! bench {
34+
( $name:ident, $size:expr ) => {
35+
pub mod $name {
36+
#[bench]
37+
pub fn bench_getrandom(b: &mut test::Bencher) {
38+
super::bench_getrandom::<{ $size }>(b);
39+
}
6840

69-
#[bench]
70-
fn bench_seed(b: &mut test::Bencher) {
71-
bench::<SEED>(b);
72-
}
73-
#[bench]
74-
fn bench_seed_init(b: &mut test::Bencher) {
75-
bench_with_init::<SEED>(b);
41+
#[bench]
42+
pub fn bench_getrandom_uninit(b: &mut test::Bencher) {
43+
super::bench_getrandom_uninit::<{ $size }>(b);
44+
}
45+
}
46+
};
7647
}
7748

78-
#[bench]
79-
fn bench_page(b: &mut test::Bencher) {
80-
bench::<PAGE>(b);
81-
}
82-
#[bench]
83-
fn bench_page_init(b: &mut test::Bencher) {
84-
bench_with_init::<PAGE>(b);
85-
}
49+
// 16 bytes (128 bits) is the size of an 128-bit AES key/nonce.
50+
bench!(aes128, 128 / 8);
8651

87-
#[bench]
88-
fn bench_large(b: &mut test::Bencher) {
89-
bench::<LARGE>(b);
90-
}
91-
#[bench]
92-
fn bench_large_init(b: &mut test::Bencher) {
93-
bench_with_init::<LARGE>(b);
94-
}
52+
// 32 bytes (256 bits) is the seed sized used for rand::thread_rng
53+
// and the `random` value in a ClientHello/ServerHello for TLS.
54+
// This is also the size of a 256-bit AES/HMAC/P-256/Curve25519 key
55+
// and/or nonce.
56+
bench!(p256, 256 / 8);
57+
58+
// A P-384/HMAC-384 key and/or nonce.
59+
bench!(p384, 384 / 8);
60+
61+
// Initializing larger buffers is not the primary use case of this library, as
62+
// this should normally be done by a userspace CSPRNG. However, we have a test
63+
// here to see the effects of a lower (amortized) syscall overhead.
64+
bench!(page, 4096);

src/3ds.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
//! Implementation for Nintendo 3DS
1010
use crate::util_libc::sys_fill_exact;
1111
use crate::Error;
12+
use core::mem::MaybeUninit;
1213

13-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
14+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1415
sys_fill_exact(dest, |buf| unsafe {
1516
libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
1617
})

src/bsd_arandom.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
//! Implementation for FreeBSD and NetBSD
1010
use crate::{util_libc::sys_fill_exact, Error};
11-
use core::ptr;
11+
use core::{mem::MaybeUninit, ptr};
1212

13-
fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
13+
fn kern_arnd(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
1414
static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
1515
let mut len = buf.len();
1616
let ret = unsafe {
@@ -30,7 +30,7 @@ fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
3030
}
3131
}
3232

33-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
33+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
3434
// getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0
3535
#[cfg(target_os = "freebsd")]
3636
{
@@ -41,7 +41,9 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
4141

4242
if let Some(fptr) = GETRANDOM.ptr() {
4343
let func: GetRandomFn = unsafe { core::mem::transmute(fptr) };
44-
return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) });
44+
return sys_fill_exact(dest, |buf| unsafe {
45+
func(buf.as_mut_ptr() as *mut u8, buf.len(), 0)
46+
});
4547
}
4648
}
4749
// Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and

src/custom.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
// except according to those terms.
88

99
//! An implementation which calls out to an externally defined function.
10-
use crate::Error;
11-
use core::num::NonZeroU32;
10+
use crate::{util::uninit_slice_fill_zero, Error};
11+
use core::{mem::MaybeUninit, num::NonZeroU32};
1212

1313
/// Register a function to be invoked by `getrandom` on unsupported targets.
1414
///
@@ -90,10 +90,16 @@ macro_rules! register_custom_getrandom {
9090
}
9191

9292
#[allow(dead_code)]
93-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
93+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
9494
extern "C" {
9595
fn __getrandom_custom(dest: *mut u8, len: usize) -> u32;
9696
}
97+
// Previously we always passed a valid, initialized slice to
98+
// `__getrandom_custom`. Ensure `dest` has been initialized for backward
99+
// compatibility with implementations that rely on that (e.g. Rust
100+
// implementations that construct a `&mut [u8]` slice from `dest` and
101+
// `len`).
102+
let dest = uninit_slice_fill_zero(dest);
97103
let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
98104
match NonZeroU32::new(ret) {
99105
None => Ok(()),

src/dragonfly.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use crate::{
1212
util_libc::{sys_fill_exact, Weak},
1313
Error,
1414
};
15+
use std::mem::MaybeUninit;
1516

16-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
17+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1718
static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
1819
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
1920

src/espidf.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88

99
//! Implementation for ESP-IDF
1010
use crate::Error;
11-
use core::ffi::c_void;
11+
use core::{ffi::c_void, mem::MaybeUninit};
1212

1313
extern "C" {
1414
fn esp_fill_random(buf: *mut c_void, len: usize) -> u32;
1515
}
1616

17-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
17+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1818
// Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`)
1919
// will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process:
2020
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html

src/fuchsia.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88

99
//! Implementation for Fuchsia Zircon
1010
use crate::Error;
11+
use core::mem::MaybeUninit;
1112

1213
#[link(name = "zircon")]
1314
extern "C" {
1415
fn zx_cprng_draw(buffer: *mut u8, length: usize);
1516
}
1617

17-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
18-
unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) }
18+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
19+
unsafe { zx_cprng_draw(dest.as_mut_ptr() as *mut u8, dest.len()) }
1920
Ok(())
2021
}

src/ios.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@
88

99
//! Implementation for iOS
1010
use crate::Error;
11-
use core::{ffi::c_void, ptr::null};
11+
use core::{ffi::c_void, mem::MaybeUninit, ptr::null};
1212

1313
#[link(name = "Security", kind = "framework")]
1414
extern "C" {
1515
fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> i32;
1616
}
1717

18-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
18+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1919
// Apple's documentation guarantees kSecRandomDefault is a synonym for NULL.
20-
let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr()) };
20+
let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr() as *mut u8) };
2121
// errSecSuccess (from SecBase.h) is always zero.
2222
if ret != 0 {
2323
Err(Error::IOS_SEC_RANDOM)

src/js.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
8-
use crate::Error;
8+
use crate::{util::uninit_slice_fill_zero, Error};
99

1010
extern crate std;
11-
use std::thread_local;
11+
use std::{mem::MaybeUninit, thread_local};
1212

1313
use js_sys::{global, Function, Uint8Array};
1414
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
@@ -28,12 +28,16 @@ thread_local!(
2828
static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
2929
);
3030

31-
pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
31+
pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
3232
RNG_SOURCE.with(|result| {
3333
let source = result.as_ref().map_err(|&e| e)?;
3434

3535
match source {
3636
RngSource::Node(n) => {
37+
// XXX(perf): `random_fill_sync` requires a `&mut [u8]` so we
38+
// have to ensure the memory in `dest` is initialized.
39+
let dest = uninit_slice_fill_zero(dest);
40+
3741
if n.random_fill_sync(dest).is_err() {
3842
return Err(Error::NODE_RANDOM_FILL_SYNC);
3943
}
@@ -49,7 +53,9 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
4953
if crypto.get_random_values(&sub_buf).is_err() {
5054
return Err(Error::WEB_GET_RANDOM_VALUES);
5155
}
52-
sub_buf.copy_to(chunk);
56+
57+
// SAFETY: `sub_buf`'s length is the same length as `chunk`
58+
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr() as *mut u8) };
5359
}
5460
}
5561
};

0 commit comments

Comments
 (0)