Skip to content
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

New lint: transmute of Vec<T> into Vec<U> with different size or alignment is UB #4515

Closed
Shnatsel opened this issue Sep 6, 2019 · 1 comment · Fixed by #4592
Closed

Comments

@Shnatsel
Copy link
Member

Shnatsel commented Sep 6, 2019

The following snippet is undefined behavior and should crash on Windows:

fn main() {
    let u32_data: Vec<u32> = vec![0, 1];
    let u8_data: Vec<u8> = unsafe { std::mem::transmute(data) }; 
}

Excerpt from the docs:

ptr's T needs to have the same size and alignment as it was allocated with.

So unlike slices you cannot transmute a type with a greater size or alignment into a type with lesser size or alignment. Even though the above snippet goes from Vec<[u32]> to Vec<[u8]>, it is UB because sizes are different. Even going from Vec<[u32]> to Vec<[u8; 4]> is UB because the alignment of the latter is different.

This is also true when casting a pointer and then feeding it into Vec::from_raw_parts() as described in #4484. I.e. this issue is not specific to transmute(), any attempt to change size and/or alignment of the allocation is UB.

This also applies to other collections that own their allocations such as VecDeque, HashMap/HashSet, BTreeMap/BTreeSet, etc. although I have not seen any transmutes of these in practice.

In the particular case of Vec you can use .as_slice() and work with that. Once you have the slice this can be accomplished using .align_to() or via a pointer cast as described in transmute_ptr_to_ptr lint.

However, there is no way to transmute the collection itself without copying, and there never will be. You must copy the data in this case.

@Shnatsel
Copy link
Member Author

Shnatsel commented Sep 6, 2019

To make this issue even more serious, this can also lead to an exploitable buffer overflow:

fn main() {
    let data: Vec<u8> = vec![1,2,3,4];
    let optimal_data: Vec<u32> = unsafe { std::mem::transmute(data) };
    println!("{:?}", optimal_data); // prints one stable number and then 3 junk numbers
}

The vector is expected to have a backing allocation of at least len * size_of(T), which is violated by increasing size_of(T). This failure case could be addressed by implementing either this lint or #4484.

bors added a commit that referenced this issue Oct 3, 2019
New lint: `unsound_collection_transmute`

changelog: Add `unsound_collection_transmute` lint

This fixes #4515
@bors bors closed this as completed in e2393b0 Oct 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant