Fix potential UB in RawTableInner::fallible_with_capacity#692
Fix potential UB in RawTableInner::fallible_with_capacity#692Amanieu merged 1 commit intorust-lang:masterfrom
RawTableInner::fallible_with_capacity#692Conversation
|
Good catch. While this isn't currently treated as UB by miri, it's definitely not good to have and worth fixing. |
|
Thanks for catching this! Looking at the code, the only thing we ever do with |
|
The plan was to eventually make I think that the main issue was assuming that the tags would be always initialized, which technically holds for every case but the beginning. So, maybe in this case it would be good to just have a fill method here; I'm indifferent. |
|
I think keeping |
|
Either way, the API here is going to get changed in refactoring once we've decided what we want to do there, so, I'm fine just making a minimal change to remove UB in the meantime. |
| fn ctrl_slice(&mut self) -> &mut [Tag] { | ||
| // SAFETY: We've initialized all control bytes, and have the correct number. | ||
| /// Gets the slice of all control bytes, as possibily uninitialized tags. | ||
| fn ctrl_slice(&mut self) -> &mut [mem::MaybeUninit<Tag>] { |
There was a problem hiding this comment.
FWIW this signature allows safe callers to de-initialize the tags. That's likely to lead to UB later. So technically the function should probably be unsafe now with a safety requirement of "don't de-initialize things". Not sure how strict hashbrown is about unsafety hygiene for private functions.
There was a problem hiding this comment.
We should be more strict about it, but at least for now, all of these APIs are being reworked. Goal is mostly to make the HashTable type be the actual implementation for HashMap and HashSet with no separate raw table API (thus, everything would need to be properly marked) but it's a slow process.
This is a fix for a potential UB in the library, that occurs anytime a HashTable is created.
The code is the following:
hashbrown/src/raw.rs
Lines 1647 to 1657 in d84a552
ctrl_slice, which before this PR returned the slice of control tags, as&mut [Tag]. However, these control bytes are uninitialised.This can be reproduced without additional tests, running the command below. The repr uses
-Zmiri-recursive-validation, an experimental flag that may be removed in the future.Importantly, this has not been decided to be undefined behaviour yet; there is ongoing discussion on the topic, see rust-lang/unsafe-code-guidelines#346. However it seems to me that it is at least unwise to pass around references to uninit values, in particular when this slice is only used for writing anyways, and the issue can be trivially avoided.
This fix thus replaces the
&mut [Tag]with&mut [MaybeUninit<Tag>], ensuring that the possibility of the slice's content being uninitialised is made explicit.Regardless of the decision on whether this should be UB or not, the code as it stands violates the safety condition of
ctrl_slice, which states "We've initialized all control bytes", since as shown above the bytes are not initialised infallible_with_capacity.hashbrown/src/raw.rs
Lines 2646 to 2649 in d84a552