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

deny transmute &[T] to &[bool] #10289

Closed
KisaragiEffective opened this issue Feb 4, 2023 · 0 comments · Fixed by #11849
Closed

deny transmute &[T] to &[bool] #10289

KisaragiEffective opened this issue Feb 4, 2023 · 0 comments · Fixed by #11849
Labels
A-lint Area: New lints

Comments

@KisaragiEffective
Copy link
Contributor

What it does

consider following example:

let single_u64: &[u64] = &[0xDEAD_BEEF_DEAD_BEEF];
let bools: &[bool] = unsafe { std::mem::transmute(single_u64) };
assert_eq!(bools.len(), 64);

but this is not true, because transmute does not change slice length.
this contains UB: Every integral value except 0 or 1 cannot be bool.
this also contains error-prone: bool's alignment is 1-byte, which may be surprizing if user thought bool is 1-bit (formally size_of::<[T; N]>() == size_of::<[bool; N * size_of::<T>]>()).

Lint Name

transmute_slice_to_bool_slice

Category

correctness, suspicious

Advantage

  • Removes potential UB.
  • Prevents error-prone.

Drawbacks

No response

Example

let single_u64: &[u64] = &[0xDEAD_BEEF_DEAD_BEEF];
let bools: &[bool] = unsafe { std::mem::transmute(single_u64) };

may be written as:

    // under #![feature(strict_provenance)] to help miri
    let bools: Vec<bool> = {
        let element_size_in_bytes = std::mem::size_of::<u64>();
        let original_slice_len = u64.len();
        let mut ret = Vec::with_capacity(original_slice_len * element_size_in_bytes * 8);
        for i in 0..original_slice_len {
            let elem: u64 = u64[i];
            let head: *const u8 = std::ptr::from_exposed_addr((&elem as *const u64).addr());
            let u8_slice: &[u8] = unsafe { std::slice::from_raw_parts(head, element_size_in_bytes) };
            let bits = element_size_in_bytes * 8;
            for bit in 0..bits {
                let is_bit = u8_slice[bit] & (1 << bit % 8) == 1;
                ret[i * element_size_in_bytes * 8 + bit] = is_bit;
            }
        }
        
        ret
    };
    
    // assert_eq!(bools, [true, true, false, true, true, true, true, false, true, false, true, false, true, true, false, true, true, false, true, true, true, true, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, true, true, false, true, false, true, false, true, true, false, true, true, false, true, true, true, true, true, false, true, true, true, false, true, true, true, true]);

but miri reports this code as an UB.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: New lints
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant