-
Notifications
You must be signed in to change notification settings - Fork 185
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
Added possibility to pass uninit arrays to random generator #271
Added possibility to pass uninit arrays to random generator #271
Conversation
I don't really know how platforms except Linux and Windows operate so here may be things which I can implement improperly. E.g. is memory which we send to JS to let it fill with random bytes must be initialized before that. |
|
||
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002; | ||
|
||
#[link(name = "bcrypt")] | ||
extern "system" { | ||
fn BCryptGenRandom( | ||
hAlgorithm: *mut c_void, | ||
pBuffer: *mut u8, | ||
pBuffer: *mut MaybeUninit<u8>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
T and MaybeUninit is equal for FFI purposes: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#layout
src/lib.rs
Outdated
// Help miri to catch mistakes. | ||
// If user calls `assume_init()` on elements, | ||
// miri would show error. | ||
#[cfg(miri)] | ||
if res.is_err() { | ||
dest.fill(MaybeUninit::uninit()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am thinking that maybe this is better to be put into rand
crate and let rand
use getrandom_to_uninit
even for initialized slices.
Also related PR: rust-random/rand#1241 |
I like this idea. Not only because of the potential utility, but because I think having methods like this to deal with uninit slices should be a pattern throughout the ecosystem. My only thing is that I wonder if using |
Personally, I would want to wait until there's a standard interface in the standard library (ReadBuf), instead of implementing our own non-standard interface to deal with uninitialized buffers. |
Also, as this is explicitly a performance improvement, could you also add some benchmarks so that we can see the perf impact of this change (vs just zeroing the uninit buffer and calling getrandom normally). I would guess the best case would be the rdrand implementation, as that's the fastest RNG we have in here IIRC |
It looks good but it is defined in std library and not in core and getrandom can be used in environments without std. |
I like this! Is there any particular reason why |
@@ -7,10 +7,12 @@ | |||
// except according to those terms. | |||
|
|||
//! Implementation for Nintendo 3DS | |||
use core::mem::MaybeUninit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: This increases our MSRV to 1.36 (current is 1.34) so is a breaking change. However, if we gate everything behind the read_buf
feature, this shouldn't be an issue, as it will require nightly anyway.
Then I think for now we should add an off-by-default
Then we could have code like this in our # Nightly-only implementation with `std::io::ReadBuf` struct.
read_buf = ["std"] and in #![cfg_attr(feature = "read_buf", feature(read_buf))]
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "read_buf")]
use std::io::ReadBuf; Then everywhere else we can just refer to |
@AngelicosPhosphoros before we get too far into this, could you add some benchmarks to show that this is actually faster when dealing with uninitialized buffers? I don't want you to waste your time if it turns out there isn't a notable difference. Specifically, I would want to compare:
I would recommend focusing on the If it turns out there's a statistically significant improvement, I think there is a path to incorporate such functionality into |
See https://godbolt.org/z/8WKn6oGrT for some example implementations using the existing API and the proposed new API. The generated code confirms that regardless of how we use the existing API, it basically becomes a call to Now it's just a question if |
@AngelicosPhosphoros actually I just realized that we don't need this PR to benchmark the cost of initialization + calling getrandom. #272 adds benchmarks that should be able to measure the cost. |
On Linux and "common" hardware memset is approximately 2 orders of magnitude faster than calling Either way, I don't think it's worth to use // It's safe to assume that the buffer gets fully filled if the function returns `Ok(())`
pub unsafe fn getrandom_raw(buf_ptr: *mut u8, buf_len: usize) -> Result<(), Error> { .. }
#[inline(always)]
pub getrandom(buf: &mut [u8]) -> Result<(), Error> {
unsafe { getrandom_raw(buf.as_mut_ptr(), buf.len()) }
} |
I think @newpavlov's suggestion in #271 (comment) is the way we will want to go with this. Closing in favor of #279 |
This can be useful to skip extra work on caller side and it is more logical because we don't really need to have initialized values to fill them with new values.