-
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
Implement raw API #279
Implement raw API #279
Conversation
@josephlr |
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.
The overall approach here seems fine, but there would be two things I would like to see:
First, is a better way to deal us not having chunks
anymore. A bunch of our code now has repeated sections where we do the same loop manipulations, I'm wondering if we could but something in util.rs
to make this nicer. Maybe something similar to sys_fill_exact
.
Second, I think we should add #![deny(unsafe_op_in_unsafe_fn)]
so that we can better see which operations are unsafe inside our (sometimes large) unsafe functions.
Yeah, it could be worthwhile. I will look into it a bit later.
Personally, I am not a huge fan of |
If the goal is to fill uninitialized arrays then the API can and should be should be using |
You would still need to use unsafe We could introduce: pub fn getrandom_uninit(buf: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> { .. } as a wrapper around |
Yes, this is what I meant. Sorry I forgot to mention the return value. If that existed,
Maybe just increase the MSRV. 1.36 was 2 years ago. |
I don't think there is anything wrong with exposing
Yeah, I guess we could do it (though I personally use a stricter MSRV policy) considering that |
I think it would be better to remove it from the API, as in general we should prefer to expose safe APIs instead of unsafe ones. I don't think the |
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 didn't review every line because my feedback here is really just trying to make a single point: Let's not replace so much safe code in this crate with a large quantity of unsafe
code. Instead, let's find a way to keep most of the logic safe. Of course we have to call into FFI functions and deal with uninitialized memory, but even still we can and should use unsafe
(and dangerous integer casts) much less often.
sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t, | ||
pub unsafe fn sys_fill_exact( | ||
mut dst: *mut u8, | ||
mut len: usize, |
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.
Again, I think this would be better as &mut [MaybeUninit<u8>]
instead. Then we could revert it back to be safe (remove unsafe
from its declaration).
|
||
pub unsafe fn raw_chunks( | ||
mut dst: *mut u8, | ||
mut len: usize, |
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.
Again, this could be made a safe function in a similar way, and all the array length arithmetic can be replaced with use of split_at
. A name change would be warranted.
pub unsafe fn getrandom_inner(dst: *mut u8, len: usize) -> Result<(), Error> { | ||
// NOTE: WASI is a 32-bit target, it can not have objects bigger | ||
// than `i32::MAX - 1`, so we do not need chunking here | ||
match random_get(dst as i32, len as i32) { |
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.
It might be true that we can't have a slice larger than i32::MAX - 1
bytes in this situation; I'm not convinced that is really true though. Regardless, this function takes a pointer and a length, not a slice, so there's no reason to think that len: usize
is constrained in the way a slice length would be. So I think the chunking should be employed and the dangerous casts should be removed. (Further, clippy lints should be employed to prohibit this use of narrowing as
.)
@newpavlov If you'd like, I could share a PR that implements the
Maybe this is only true of the length is small enough to be a valid slice length. But, again, the current implementation is already making such assumptions. |
In my opinion, we should go for the best of both worlds here. Have a raw pointer API and a |
@briansmith @notgull I've opened #286 to figure out the consensus for increasing the MSRV of |
My position is based on the following guideline: It is important to minimize the amount of |
I agree with @briansmith here, I think if we make more use of |
let f: fn(&mut [u8]) -> Result<(), $crate::Error> = $path; | ||
let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) }; | ||
let slice = unsafe { ::core::slice::from_raw_parts_mut(dst, len) }; |
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 think this is incorrect and fixing it would break backward compatibility.
getrandom_raw
can be called with a pointer to uninitialized memory. So, we cannot legally construct a &mut [u8]
slice from that pointer since we're only allowed to construct such a slice if the pointer points to initialized memory.
The function $path
has a parameter of type &mut [u8]
but to support the _raw
API and/or the _uninit
API it must take &mut [MaybeUninit<u8>]
or a raw pointer and length instead.
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.
One solution would be to have register_custom_getrandom!
pre-initialize the output buffer before constructing slice
, similar to what the JS code does. We could add a register_custom_getrandom_uninit!
that does the same thing without the redundant zero pre-initialization that uses MaybeUninit<u8>
instead.
I put up a draft PR #291 that does that. Again, my main goal there is to minimize the amount of |
Closing it in favor of #291 |
A simpler alternative to #271.
It introduces
getrandom_raw
function with the following signature:Note that
getrandom
is built on top of this function and allgetrandom_impl
s also now implement the pointer-based API. It fits quite well for all backends except thejs
one.It allows to fill uninitialized arrays with random data.
Additionally, as a safe wrapper around
getrandom_raw
this PR addsgetrandom_uninit
:Because of
MaybeUninit
MSRV gets bumped to 1.36 (the same MSRV asrand
).I have reworked the benchmark and I get the following results:
On my (Linux-based) PC the difference between
getrandom
andgetrandom_raw
is less than 1% and barely noticeable. The former zeroizes stack-based buffer before callinggetrandom
, while the latter usesMaybeUninit
to skip the unnecessary initialization step.