Skip to content

Commit bafe2e2

Browse files
committed
Implement raw API
1 parent 9a64857 commit bafe2e2

23 files changed

+188
-161
lines changed

benches/mod.rs

+11-48
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,22 @@
11
#![feature(test)]
22
extern crate test;
33

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]>);
11-
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-
}
26-
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-
}
32-
33-
// Used to benchmark the throughput of getrandom in an optimal scenario.
34-
// The buffer is hot, and does not require initialization.
354
#[inline(always)]
365
fn bench<const N: usize>(b: &mut test::Bencher) {
37-
let mut ab = AlignedBuffer::<N>::new();
38-
let buf = ab.buf();
396
b.iter(|| {
7+
let mut buf = [0u8; N];
408
getrandom::getrandom(&mut buf[..]).unwrap();
419
test::black_box(&buf);
4210
});
4311
b.bytes = N as u64;
4412
}
4513

46-
// Used to benchmark the throughput of getrandom is a slightly less optimal
47-
// scenario. The buffer is still hot, but requires initialization.
4814
#[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();
15+
fn bench_raw<const N: usize>(b: &mut test::Bencher) {
5216
b.iter(|| {
53-
for byte in buf.iter_mut() {
54-
*byte = 0;
55-
}
56-
getrandom::getrandom(&mut buf[..]).unwrap();
17+
let mut buf = core::mem::MaybeUninit::<[u8; N]>::uninit();
18+
// TODO: use `cast` on MSRV bump to 1.38
19+
unsafe { getrandom::getrandom_raw(buf.as_mut_ptr() as *mut u8, N).unwrap() };
5720
test::black_box(&buf);
5821
});
5922
b.bytes = N as u64;
@@ -71,24 +34,24 @@ fn bench_seed(b: &mut test::Bencher) {
7134
bench::<SEED>(b);
7235
}
7336
#[bench]
74-
fn bench_seed_init(b: &mut test::Bencher) {
75-
bench_with_init::<SEED>(b);
37+
fn bench_seed_raw(b: &mut test::Bencher) {
38+
bench_raw::<SEED>(b);
7639
}
7740

7841
#[bench]
7942
fn bench_page(b: &mut test::Bencher) {
8043
bench::<PAGE>(b);
8144
}
8245
#[bench]
83-
fn bench_page_init(b: &mut test::Bencher) {
84-
bench_with_init::<PAGE>(b);
46+
fn bench_page_raw(b: &mut test::Bencher) {
47+
bench_raw::<PAGE>(b);
8548
}
8649

8750
#[bench]
8851
fn bench_large(b: &mut test::Bencher) {
8952
bench::<LARGE>(b);
9053
}
9154
#[bench]
92-
fn bench_large_init(b: &mut test::Bencher) {
93-
bench_with_init::<LARGE>(b);
55+
fn bench_large_raw(b: &mut test::Bencher) {
56+
bench_raw::<LARGE>(b);
9457
}

src/3ds.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
use crate::util_libc::sys_fill_exact;
1111
use crate::Error;
1212

13-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
14-
sys_fill_exact(dest, |buf| unsafe {
15-
libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
13+
pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> {
14+
// TODO: use `cast` on MSRV bump to 1.38
15+
sys_fill_exact(dst, len, |dst, len| {
16+
libc::getrandom(dst as *mut libc::c_void, len, 0)
1617
})
1718
}

src/bsd_arandom.rs

+18-17
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,25 @@
1010
use crate::{util_libc::sys_fill_exact, Error};
1111
use core::ptr;
1212

13-
fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
13+
unsafe fn kern_arnd(dst: *mut u8, mut len: usize) -> libc::ssize_t {
1414
static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
15-
let mut len = buf.len();
16-
let ret = unsafe {
17-
libc::sysctl(
18-
MIB.as_ptr(),
19-
MIB.len() as libc::c_uint,
20-
buf.as_mut_ptr() as *mut _,
21-
&mut len,
22-
ptr::null(),
23-
0,
24-
)
25-
};
15+
// TODO: use `cast` on MSRV bump to 1.38
16+
let ret = libc::sysctl(
17+
MIB.as_ptr(),
18+
MIB.len() as libc::c_uint,
19+
dst as *mut libc::c_void,
20+
&mut len,
21+
ptr::null(),
22+
0,
23+
);
2624
if ret == -1 {
2725
-1
2826
} else {
2927
len as libc::ssize_t
3028
}
3129
}
3230

33-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
31+
pub unsafe fn getrandom_inner(mut dst: *mut u8, mut len: usize) -> Result<(), Error> {
3432
// getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0
3533
#[cfg(target_os = "freebsd")]
3634
{
@@ -40,14 +38,17 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
4038
unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
4139

4240
if let Some(fptr) = GETRANDOM.ptr() {
43-
let func: GetRandomFn = unsafe { core::mem::transmute(fptr) };
44-
return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) });
41+
let func: GetRandomFn = core::mem::transmute(fptr);
42+
return sys_fill_exact(dst, len, |dst, len| func(dst, len, 0));
4543
}
4644
}
4745
// Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and
4846
// older NetBSD kernels will fail on longer buffers.
49-
for chunk in dest.chunks_mut(256) {
50-
sys_fill_exact(chunk, kern_arnd)?
47+
while len != 0 {
48+
let chunk_len = core::cmp::min(len, 256);
49+
sys_fill_exact(dst, chunk_len, |dst, len| kern_arnd(dst, len))?;
50+
dst = dst.add(chunk_len);
51+
len -= chunk_len;
5152
}
5253
Ok(())
5354
}

src/custom.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ macro_rules! register_custom_getrandom {
7878
($path:path) => {
7979
// We use an extern "C" function to get the guarantees of a stable ABI.
8080
#[no_mangle]
81-
extern "C" fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
81+
extern "C" fn __getrandom_custom(dst: *mut u8, len: usize) -> u32 {
8282
let f: fn(&mut [u8]) -> Result<(), $crate::Error> = $path;
83-
let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) };
83+
let slice = unsafe { ::core::slice::from_raw_parts_mut(dst, len) };
8484
match f(slice) {
8585
Ok(()) => 0,
8686
Err(e) => e.code().get(),
@@ -90,11 +90,11 @@ 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(dst: *mut u8, len: usize) -> Result<(), Error> {
9494
extern "C" {
95-
fn __getrandom_custom(dest: *mut u8, len: usize) -> u32;
95+
fn __getrandom_custom(dst: *mut u8, len: usize) -> u32;
9696
}
97-
let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
97+
let ret = unsafe { __getrandom_custom(dst, len) };
9898
match NonZeroU32::new(ret) {
9999
None => Ok(()),
100100
Some(code) => Err(Error::from(code)),

src/dragonfly.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ use crate::{
1313
Error,
1414
};
1515

16-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
16+
pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> {
1717
static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
1818
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
1919

2020
// getrandom(2) was introduced in DragonflyBSD 5.7
2121
if let Some(fptr) = GETRANDOM.ptr() {
22-
let func: GetRandomFn = unsafe { core::mem::transmute(fptr) };
23-
return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) });
22+
let func: GetRandomFn = core::mem::transmute(fptr);
23+
sys_fill_exact(dst, len, |dst, len| func(dst, len, 0))
2424
} else {
25-
use_file::getrandom_inner(dest)
25+
use_file::getrandom_inner(dst, len)
2626
}
2727
}

src/espidf.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ 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 unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> 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
2121
//
2222
// However tracking if some of these entropy sources is enabled is way too difficult to implement here
23-
unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) };
24-
23+
// TODO: use `cast` on MSRV bump to 1.38
24+
esp_fill_random(dst as *mut c_void, len);
2525
Ok(())
2626
}

src/fuchsia.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extern "C" {
1414
fn zx_cprng_draw(buffer: *mut u8, length: usize);
1515
}
1616

17-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
18-
unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) }
17+
pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> {
18+
zx_cprng_draw(dst, len);
1919
Ok(())
2020
}

src/ios.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ 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 unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> 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 = SecRandomCopyBytes(null(), len, dst);
2121
// errSecSuccess (from SecBase.h) is always zero.
2222
if ret != 0 {
2323
Err(Error::IOS_SEC_RANDOM)

src/lib.rs

+34-4
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ cfg_if! {
251251
}
252252
}
253253

254-
/// Fill `dest` with random bytes from the system's preferred random number
254+
/// Fill `dst` with random bytes from the system's preferred random number
255255
/// source.
256256
///
257257
/// This function returns an error on any failure, including partial reads. We
@@ -264,9 +264,39 @@ cfg_if! {
264264
/// In general, `getrandom` will be fast enough for interactive usage, though
265265
/// significantly slower than a user-space CSPRNG; for the latter consider
266266
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
267-
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
268-
if dest.is_empty() {
267+
pub fn getrandom(dst: &mut [u8]) -> Result<(), Error> {
268+
if dst.is_empty() {
269269
return Ok(());
270270
}
271-
imp::getrandom_inner(dest)
271+
unsafe { getrandom_raw(dst.as_mut_ptr(), dst.len()) }
272+
}
273+
274+
/// Raw version of the `getrandom` function.
275+
///
276+
/// If this function returns `Ok(())`, then it's safe to assume that the
277+
/// `len` random bytes were written to the memory pointed by `dst`.
278+
///
279+
/// # Safety
280+
///
281+
/// `dst` MUST be [valid] for writes of `len` bytes.
282+
///
283+
/// [valid]: core::ptr#safety
284+
///
285+
/// # Examples
286+
///
287+
/// ```ignore
288+
/// # // We ignore this doctest because `MaybeUninit` was stabilized
289+
/// # // in Rust 1.36, while this crate has MSRV equal to 1.34.
290+
/// # fn main() -> Result<(), getrandom::Error> {
291+
/// const BUF_SIZE: usize = 1024;
292+
///
293+
/// let buf: [u8; BUF_SIZE] = unsafe {
294+
/// let mut buf = core::mem::MaybeUninit::uninit();
295+
/// getrandom::getrandom_raw(buf.as_mut_ptr() as *mut u8, BUF_SIZE)?;
296+
/// buf.assume_init()
297+
/// };
298+
/// # Ok(()) }
299+
/// ```
300+
pub unsafe fn getrandom_raw(dst: *mut u8, len: usize) -> Result<(), Error> {
301+
imp::getrandom_inner(dst, len)
272302
}

src/linux_android.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ use crate::{
1313
{use_file, Error},
1414
};
1515

16-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
16+
pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> {
1717
// getrandom(2) was introduced in Linux 3.17
1818
static HAS_GETRANDOM: LazyBool = LazyBool::new();
1919
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
20-
sys_fill_exact(dest, |buf| unsafe {
21-
getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
20+
// TODO: use `cast` on MSRV bump to 1.38
21+
sys_fill_exact(dst, len, |dst, len| {
22+
getrandom(dst as *mut libc::c_void, len, 0)
2223
})
2324
} else {
24-
use_file::getrandom_inner(dest)
25+
use_file::getrandom_inner(dst, len)
2526
}
2627
}
2728

src/macos.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,24 @@ use core::mem;
1616

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

19-
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
19+
pub unsafe fn getrandom_inner(mut dst: *mut u8, mut len: usize) -> Result<(), Error> {
2020
// getentropy(2) was added in 10.12, Rust supports 10.7+
2121
static GETENTROPY: Weak = unsafe { Weak::new("getentropy\0") };
2222
if let Some(fptr) = GETENTROPY.ptr() {
23-
let func: GetEntropyFn = unsafe { mem::transmute(fptr) };
24-
for chunk in dest.chunks_mut(256) {
25-
let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) };
23+
let func: GetEntropyFn = mem::transmute(fptr);
24+
while len != 0 {
25+
let chunk_len = core::cmp::min(len, 256);
26+
let ret = func(dst, chunk_len);
2627
if ret != 0 {
2728
return Err(last_os_error());
2829
}
30+
dst = dst.add(chunk_len);
31+
len -= chunk_len;
2932
}
3033
Ok(())
3134
} else {
3235
// We fallback to reading from /dev/random instead of SecRandomCopyBytes
3336
// to avoid high startup costs and linking the Security framework.
34-
use_file::getrandom_inner(dest)
37+
use_file::getrandom_inner(dst, len)
3538
}
3639
}

src/openbsd.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@
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 unsafe fn getrandom_inner(mut dst: *mut u8, mut len: usize) -> Result<(), Error> {
1313
// getentropy(2) was added in OpenBSD 5.6, so we can use it unconditionally.
14-
for chunk in dest.chunks_mut(256) {
15-
let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) };
14+
while len != 0 {
15+
let chunk_len = core::cmp::min(len, 256);
16+
// TODO: use `cast` on MSRV bump to 1.38
17+
let ret = libc::getentropy(dst as *mut libc::c_void, chunk_len);
1618
if ret == -1 {
1719
return Err(last_os_error());
1820
}
21+
dst = dst.add(chunk_len);
22+
len -= chunk_len;
1923
}
2024
Ok(())
2125
}

0 commit comments

Comments
 (0)