Replace std::mem::uninitialized with MaybeUninit#1027
Replace std::mem::uninitialized with MaybeUninit#1027goddessfreya merged 5 commits intorust-windowing:masterfrom
std::mem::uninitialized with MaybeUninit#1027Conversation
|
All uses of |
|
|
|
I was not aware that even an uninitialized |
You know you can "delete" a comment :) |
| @@ -25,16 +25,16 @@ impl From<ffi::XIModifierState> for ModifiersState { | |||
|
|
|||
| pub struct PointerState<'a> { | |||
There was a problem hiding this comment.
It turns out that some of these private fields are unused. rustc wasn't detecting this before because the struct was being filled with &mut pointer_state.field references and this was seen as a use. I've commented out the fields as it should have no effect on the program. I could instead make them public or add #[allow(dead_code)] to the struct.
There was a problem hiding this comment.
I kept them in both 1) to make allocation easier, and 2) so that contributors can more easily discover/utilize functionality they might need (the idea is to provide a robust internal API). I'd prefer they be kept in, though with comments explaining the rationale.
There was a problem hiding this comment.
Fair enough. I've restored the fields and marked them pub to silence the warnings.
|
@zegentzy I believe I have resolved all of the undefined behavior issues. |
|
There should probably be a more thorough audit of I think that most of these cases should either be replaced with |
|
@aloucks in what ways is |
goddessfreya
left a comment
There was a problem hiding this comment.
This code looks about right. Can we try replacing some of them with MaybeUninit::zeroed()?
|
@francesca64 Reading from unitialized memory triggers UB. Reading from zero-initialized memory only triggers UB if that is not a valid state. Since most (all?) of these variables are integers, accidentally reading from them if they get zeroed-initialized, instead of uninitialized, will help us avoid accidental UB. |
In general, you should avoid Why would you prefer |
I don't think you even have to read from it for it to be considered UB. The addition of |
|
@aloucks According to the docs:
So, operations that would normally trigger UB on uninitialized memory will not trigger UB on zero-initialized types, if zero is a valid initialization of the type. |
I'd rather we zero initialize whole integer-only structs than manually set their contents to zero. |
Yes, absolutely - that's why I was saying prefer it over uninitialized and prefer |
|
|
Yes, but since That said, if brevity is preferred, I'm not opposed to changing it. |
|
What I had originally meant by auditing the use of For example, pub fn query_pointer(
&self,
window: ffi::Window,
device_id: c_int,
) -> Result<PointerState<'_>, XError> {
let mut root = 0;
let mut child = 0;
let mut root_x = 0.0;
let mut root_y = 0.0;
let mut win_x = 0.0;
let mut win_y = 0.0;
let mut group = Default::default();
let mut buttons = Default::default();
let mut modifiers = Default::default();
let relative_to_window = unsafe { (self.xinput2.XIQueryPointer)(
self.display,
device_id,
window,
&mut root,
&mut child,
&mut root_x,
&mut root_y,
&mut win_x,
&mut win_y,
&mut buttons,
&mut modifiers,
&mut group,
) == ffi::True };
if let Err(err) = self.check_errors() {
Err(err)
} else {
Ok(PointerState {
xconn: self,
root,
child,
root_x,
root_y,
win_x,
relative_to_window,
buttons,
modifiers,
win_y,
group
})
}
}Interestingly enough now that we're creating that struct with 100% safe code we find that most of those fields aren't even being used. The overzealous usage of |
* Prefer safe zero-initialization using `Default`, when possible * Zero-initialize integers and floats using `0` or `0.0` * Use `MaybeUninit::uninit` for large byte buffers and union types * Use `MaybeUninit::uninit` when the resulting value is ignored
|
I've pushed a commit reforming usage of
|
| if let Ok(ref xconn) = *xconn_lock { | ||
| let mut buf: [c_char; 1024] = mem::uninitialized(); | ||
| // `assume_init` is safe here because the array consists of `MaybeUninit` values, | ||
| // which do not require initialization. |
There was a problem hiding this comment.
This could be replaced with
let mut buf = [0 as c_char; 1024];There was a problem hiding this comment.
Why zero-initialize a KB of memory? If we can't trust a C library to copy data into a buffer and accurately report the number of bytes written, why are we using C libraries at all?
| pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints<'_>, XError> { | ||
| let size_hints = self.alloc_size_hints(); | ||
| let mut supplied_by_user: c_long = unsafe { mem::uninitialized() }; | ||
| let mut supplied_by_user = MaybeUninit::uninit(); |
There was a problem hiding this comment.
This is just an integer. There is no need for uninit.
You can just set it normally (and change the param back to &mut supplied_by_user below:
let mut supplied_by_user = 0;There was a problem hiding this comment.
The value is ignored. There's no unsafe. There's no undefined behavior.
Why zero-initialize a variable that is never read?
| let (_, _, new_count) = self.lookup_utf8_inner(ic, key_event, &mut buffer); | ||
| // `assume_init` is safe here because the array consists of `MaybeUninit` values, | ||
| // which do not require initialization. | ||
| let mut buffer: [MaybeUninit<u8>; TEXT_BUFFER_SIZE] = |
There was a problem hiding this comment.
This could be replaced with
let mut buffer = [0_u8; TEXT_BUFFER_SIZE];| // 64 would suffice for Linux, but 256 will be enough everywhere (as per SUSv2). For instance, this is | ||
| // the limit defined by OpenBSD. | ||
| const MAXHOSTNAMELEN: usize = 256; | ||
| let mut hostname: [c_char; MAXHOSTNAMELEN] = mem::uninitialized(); |
There was a problem hiding this comment.
This could be replaced with
let mut hostname = [0 as c_char; MAXHOSTNAMELEN];| let cursor = unsafe { | ||
| // We don't care about this color, since it only fills bytes | ||
| // in the pixmap which are not 0 in the mask. | ||
| let dummy_color: ffi::XColor = mem::uninitialized(); |
There was a problem hiding this comment.
Why not mem::zeroed() and be done with it? (or Default::default(), which is probably the same)
There was a problem hiding this comment.
Same as above, the value is not used.
goddessfreya
left a comment
There was a problem hiding this comment.
Assuming https://github.com/rust-windowing/winit/pull/1027/files/15409a1600103be3899aba7d6a576a48bedfe5c0#diff-957c45ae2e25aaf20b1e6229cc7aa195R423 can't be changed to use Default, this lgtm.
|
Apologies if I've missed something. Are there any unresolved issues with this PR? |
…1027) * Replace `std::mem::uninitialized` with `MaybeUninit` * Avoid undefined behavior when using `MaybeUninit` * Restore unused `PointerState` fields as internally public * Zero-initialize some struct values in Xlib FFI calls * Reform usage of `MaybeUninit` in Xlib FFI * Prefer safe zero-initialization using `Default`, when possible * Zero-initialize integers and floats using `0` or `0.0` * Use `MaybeUninit::uninit` for large byte buffers and union types * Use `MaybeUninit::uninit` when the resulting value is ignored
…ndowing#1027)" This reverts commit 7daf146.
Closes #1025
cargo fmthas been run on this branchCHANGELOG.mdif knowledge of this change could be valuable to users