Skip to content

Commit 9fdc800

Browse files
committed
Add getrandom_uninit_slice(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. Fixes #226.
1 parent cfdad53 commit 9fdc800

27 files changed

+225
-74
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

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ 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_slice` [#TODO]
10+
11+
### Breaking Changes
12+
- Update MSRV to 1.36 [#TODO]
13+
714
## [0.2.7] - 2022-06-14
815
### Changed
916
- Update `wasi` dependency to `0.11` [#253]

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

+34
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ extern crate test;
33

44
use std::{
55
alloc::{alloc_zeroed, dealloc, Layout},
6+
mem::{self, MaybeUninit},
67
ptr::NonNull,
78
};
89

@@ -59,6 +60,20 @@ fn bench_with_init<const N: usize>(b: &mut test::Bencher) {
5960
b.bytes = N as u64;
6061
}
6162

63+
// Used to benchmark the benefit of `getrandom_uninit` compared to
64+
// zero-initializing a buffer and then using `getrandom` (`bench_with_init`
65+
// above).
66+
#[inline(always)]
67+
fn bench_uninit<const N: usize>(b: &mut test::Bencher) {
68+
let mut ab = AlignedBuffer::<N>::new();
69+
let buf = ab.buf();
70+
// SAFETY: `buf` doesn't escape this scope.
71+
let buf = unsafe { slice_as_uninit_mut(buf) };
72+
b.iter(|| {
73+
let _ = getrandom::getrandom_uninit_slice(buf);
74+
})
75+
}
76+
6277
// 32 bytes (256-bit) is the seed sized used for rand::thread_rng
6378
const SEED: usize = 32;
6479
// Common size of a page, 4 KiB
@@ -74,6 +89,10 @@ fn bench_seed(b: &mut test::Bencher) {
7489
fn bench_seed_init(b: &mut test::Bencher) {
7590
bench_with_init::<SEED>(b);
7691
}
92+
#[bench]
93+
fn bench_seed_uninit(b: &mut test::Bencher) {
94+
bench_uninit::<SEED>(b);
95+
}
7796

7897
#[bench]
7998
fn bench_page(b: &mut test::Bencher) {
@@ -83,6 +102,10 @@ fn bench_page(b: &mut test::Bencher) {
83102
fn bench_page_init(b: &mut test::Bencher) {
84103
bench_with_init::<PAGE>(b);
85104
}
105+
#[bench]
106+
fn bench_page_uninit(b: &mut test::Bencher) {
107+
bench_uninit::<PAGE>(b);
108+
}
86109

87110
#[bench]
88111
fn bench_large(b: &mut test::Bencher) {
@@ -92,3 +115,14 @@ fn bench_large(b: &mut test::Bencher) {
92115
fn bench_large_init(b: &mut test::Bencher) {
93116
bench_with_init::<LARGE>(b);
94117
}
118+
#[bench]
119+
fn bench_large_uninit(b: &mut test::Bencher) {
120+
bench_uninit::<LARGE>(b);
121+
}
122+
123+
// TODO: Safety note.
124+
#[inline(always)]
125+
unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
126+
// SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
127+
mem::transmute(slice)
128+
}

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

+7-5
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,18 +30,20 @@ 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
{
37-
use crate::util_libc::Weak;
37+
use crate::{util::uninit_slice_as_mut_ptr, util_libc::Weak};
3838
static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
3939
type GetRandomFn =
4040
unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
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(uninit_slice_as_mut_ptr(buf), 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

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

99
//! Implementation for ESP-IDF
10-
use crate::Error;
11-
use core::ffi::c_void;
10+
use crate::{util::uninit_slice_as_mut_ptr, Error};
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

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

99
//! Implementation for Fuchsia Zircon
10-
use crate::Error;
10+
use crate::{util::uninit_slice_as_mut_ptr, 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(uninit_slice_as_mut_ptr(dest), dest.len()) }
1920
Ok(())
2021
}

src/ios.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
// except according to those terms.
88

99
//! Implementation for iOS
10-
use crate::Error;
11-
use core::{ffi::c_void, ptr::null};
10+
use crate::{util::uninit_slice_as_mut_ptr, Error};
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(), uninit_slice_as_mut_ptr(dest)) };
2121
// errSecSuccess (from SecBase.h) is always zero.
2222
if ret != 0 {
2323
Err(Error::IOS_SEC_RANDOM)

src/js.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
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::{
9+
util::{uninit_slice_as_mut_ptr, uninit_slice_fill_zero},
10+
Error,
11+
};
912

1013
extern crate std;
11-
use std::thread_local;
14+
use std::{mem::MaybeUninit, thread_local};
1215

1316
use js_sys::{global, Function, Uint8Array};
1417
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
@@ -28,12 +31,16 @@ thread_local!(
2831
static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
2932
);
3033

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

3538
match source {
3639
RngSource::Node(n) => {
40+
// XXX(perf): `random_fill_sync` requires a `&mut [u8]` so we
41+
// have to ensure the memory in `dest` is initialized.
42+
let dest = uninit_slice_fill_zero(dest);
43+
3744
if n.random_fill_sync(dest).is_err() {
3845
return Err(Error::NODE_RANDOM_FILL_SYNC);
3946
}
@@ -49,7 +56,9 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
4956
if crypto.get_random_values(&sub_buf).is_err() {
5057
return Err(Error::WEB_GET_RANDOM_VALUES);
5158
}
52-
sub_buf.copy_to(chunk);
59+
60+
// SAFETY: `sub_buf`'s length is the same length as `chunk`
61+
unsafe { sub_buf.raw_copy_to_ptr(uninit_slice_as_mut_ptr(chunk)) };
5362
}
5463
}
5564
};

src/lib.rs

+34-5
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@
187187
#[macro_use]
188188
extern crate cfg_if;
189189

190+
use crate::util::{slice_as_uninit_mut, slice_assume_init_mut};
191+
use core::mem::MaybeUninit;
192+
190193
mod error;
191194
mod util;
192195
// To prevent a breaking change when targets are added, we always export the
@@ -200,7 +203,8 @@ pub use crate::error::Error;
200203

201204
// System-specific implementations.
202205
//
203-
// These should all provide getrandom_inner with the same signature as getrandom.
206+
// These should all provide getrandom_inner with the signature
207+
// `fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error>`.
204208
cfg_if! {
205209
if #[cfg(any(target_os = "emscripten", target_os = "haiku",
206210
target_os = "redox"))] {
@@ -284,9 +288,34 @@ cfg_if! {
284288
/// In general, `getrandom` will be fast enough for interactive usage, though
285289
/// significantly slower than a user-space CSPRNG; for the latter consider
286290
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
291+
#[inline]
287292
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
288-
if dest.is_empty() {
289-
return Ok(());
290-
}
291-
imp::getrandom_inner(dest)
293+
// SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape.
294+
getrandom_uninit_slice(unsafe { slice_as_uninit_mut(dest) }).map(|_| ())
295+
}
296+
297+
/// Version of the `getrandom` function which fills `dest` with random bytes
298+
/// returns a mutable reference to those bytes.
299+
///
300+
/// On successful completion this function is guaranteed to return a slice
301+
/// which points to the same memory as `dest` and has the same length.
302+
/// In other words, it's safe to assume that `dest` is initialized after
303+
/// this function has returned `Ok`.
304+
///
305+
/// # Examples
306+
///
307+
/// ```ignore
308+
/// # // We ignore this test since `uninit_array` is unstable.
309+
/// #![feature(maybe_uninit_uninit_array)]
310+
/// # fn main() -> Result<(), getrandom::Error> {
311+
/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>();
312+
/// let buf: &mut [u8] = getrandom::getrandom_uninit_slice(&mut buf)?;
313+
/// # Ok(()) }
314+
/// ```
315+
#[inline]
316+
pub fn getrandom_uninit_slice(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> {
317+
imp::getrandom_inner(dest)?;
318+
// SAFETY: `dest` has been fully initialized by `imp::getrandom_inner`
319+
// since it returned `Ok`.
320+
Ok(unsafe { slice_assume_init_mut(dest) })
292321
}

src/linux_android.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use crate::{
1212
util_libc::{last_os_error, sys_fill_exact},
1313
{use_file, Error},
1414
};
15+
use core::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
// getrandom(2) was introduced in Linux 3.17
1819
static HAS_GETRANDOM: LazyBool = LazyBool::new();
1920
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {

src/macos.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@
99
//! Implementation for macOS
1010
use crate::{
1111
use_file,
12+
util::uninit_slice_as_mut_ptr,
1213
util_libc::{last_os_error, Weak},
1314
Error,
1415
};
15-
use core::mem;
16+
use core::mem::{self, MaybeUninit};
1617

1718
type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int;
1819

19-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
20+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
2021
// getentropy(2) was added in 10.12, Rust supports 10.7+
2122
static GETENTROPY: Weak = unsafe { Weak::new("getentropy\0") };
2223
if let Some(fptr) = GETENTROPY.ptr() {
2324
let func: GetEntropyFn = unsafe { mem::transmute(fptr) };
2425
for chunk in dest.chunks_mut(256) {
25-
let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) };
26+
let ret = unsafe { func(uninit_slice_as_mut_ptr(chunk), chunk.len()) };
2627
if ret != 0 {
2728
return Err(last_os_error());
2829
}

src/openbsd.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//! Implementation for OpenBSD
1010
use crate::{util_libc::last_os_error, Error};
1111

12-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
12+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1313
// getentropy(2) was added in OpenBSD 5.6, so we can use it unconditionally.
1414
for chunk in dest.chunks_mut(256) {
1515
let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) };

0 commit comments

Comments
 (0)