Fixes UB in stack::generator
: Aliasing violations and references to uninit memory.
#19
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
While looking through the code I noticed some issues with unsound usage of
MaybeUninit
as well as one aliasing violation. Instead of opening an issue I decided to try and fix it myself. I have not looked at the the other modules, so there may be similar issues there.Issues
genawaiter/src/stack/generator.rs
Lines 92 to 93 in 3e9652a
Here
as_mut_ptr
returns a*mut T
to uninitialized memory. De-referencing it is UB, which is also unnecessary, since it is turned back into a raw pointer anyways in the same line.Lines 96 and 99 cause UB in a similar manner, i.e., by de-referencing a pointer to uninitialized memory.
genawaiter/src/stack/generator.rs
Lines 88 to 99 in 3e9652a
The aliasing issue is more subtle:
Gen::new
receives a mutable reference to aShelf
and stores it within itself as aPin<&mut _>
, however, the future that is created byproducer
receives a shared reference to theAirlock
, which it may store within itself. This is a bit weird, since it is a reference into the same struct, but I believe it constitutes a violation of the aliasing rules, because there exists at the same time a (pinned) mutable reference to theShelf
and a shared reference to theAirlock
within the sameShelf
.Changes
Airlock
is initialized regularly, without the use ofMaybeUninit
future
is initialized correctly through a raw pointer writeshelf
borrow is split into a mutable and pinned borrow offuture
and a shared borrow ofairlock
, which means the aliasing rules are maintained (I think)Discussion
I am sure, if
Gen::new
has to be an unsafe function. I assume that it would be possible to violate memory safety if it were possible to find a way to get theproducer
closure to move theCo
instance out and in between theShelf
and theGen
like so:I have not found a way to get something like this to compile however, so maybe
Gen::new
could be declared as safe?EDIT:
Having read through the other PR about making
Co::yield_
take&mut self
I've seen it is indeed possible to let theco
instance escape, but sinceairlock
is now initialized regularly and only dropped alongside theshelf
, I think it is safe to let it escape the closure and try to yield stuff from it after the the generator is complete, the code will simply panic without any violations of memory safety.