Fixes UB in stack::generator: Aliasing violations and references to uninit memory.#19
Fixes UB in stack::generator: Aliasing violations and references to uninit memory.#19oliver-giersch wants to merge 3 commits intowhatisaphone:masterfrom
stack::generator: Aliasing violations and references to uninit memory.#19Conversation
…ory and aliasing of mutable references.
… a shared and a unique part
|
Thanks for the PR! It's great to have more eyes on the The first issue is definitely technically UB – though for now it's generally accepted as the best way to express the specific case of The second one I'm not so sure is UB. It's perfectly fine to have aliasing shared references pointing inside a mutable reference (example). The I'm happy to accept this PR, since either way it's a nice simplification of the code. I think you are right about I resolved the merge conflict and rebased your changes onto master. Thanks for contributing! |
|
Thanks for merging and apologies for not resolving the conflict myself, but I could not figure out what the issue was, since I actually had run rustfmt on everything. I was also not totally certain if the way the borrow of |
While looking through the code I noticed some issues with unsound usage of
MaybeUninitas 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_ptrreturns a*mut Tto 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::newreceives a mutable reference to aShelfand stores it within itself as aPin<&mut _>, however, the future that is created byproducerreceives 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 theShelfand a shared reference to theAirlockwithin the sameShelf.Changes
Airlockis initialized regularly, without the use ofMaybeUninitfutureis initialized correctly through a raw pointer writeshelfborrow is split into a mutable and pinned borrow offutureand a shared borrow ofairlock, which means the aliasing rules are maintained (I think)Discussion
I am sure, if
Gen::newhas 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 theproducerclosure to move theCoinstance out and in between theShelfand theGenlike so:I have not found a way to get something like this to compile however, so maybe
Gen::newcould be declared as safe?EDIT:
Having read through the other PR about making
Co::yield_take&mut selfI've seen it is indeed possible to let thecoinstance escape, but sinceairlockis 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.