Skip to content

New lint: transmute(Vec) is undefined behavior #4484

Closed
@Shnatsel

Description

@Shnatsel

Documentation on std::mem::transmute() specifically calls out that transmuting a Vec of any type is UB.

Code with UB from documentation:

let store = [0, 1, 2, 3];
let mut v_orig = store.iter().collect::<Vec<&i32>>();

// Using transmute: this is Undefined Behavior, and a bad idea.
let v_transmuted = unsafe {
    std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(v_orig)
};

Correct code from documentation (assuming interior types can be transmuted):

let store = [0, 1, 2, 3];
let mut v_orig = store.iter().collect::<Vec<&i32>>();
// Using from_raw_parts
let v_from_raw = unsafe {
    Vec::from_raw_parts(v_orig.as_mut_ptr() as *mut Option<&i32>,
                        v_orig.len(),
                        v_orig.capacity())
};
std::mem::forget(v_orig);

What exactly is wrong with it is not currently documented (I've opened rust-lang/rust#64073 to clarify this) but my understanding is that the problem stems from the following:

Most fundamentally, Vec is and always will be a (pointer, capacity, length) triplet. No more, no less. The order of these fields is completely unspecified, and you should use the appropriate methods to modify these.

I.e. while it's safe to transmute the data on the heap assuming the types are compatible, it's never safe to transmute the (pointer, capacity, length) triplet on the stack.

I'm not 100% convinced that the "correct" code proposed in documentation correctly upholds aliasing invariants. It's possible that std::mem::forget() must be called before the new Vec is constructed, or that it's only safe to do with ManuallyDrop.

cc @RalfJung

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintArea: New lintsL-correctnessLint: Belongs in the correctness lint group

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions