Skip to content

std::mem::transmute may mutate result #96140

Closed
@jmeggitt

Description

@jmeggitt

Example Code

During some experimenting in the Rust playground, I noticed something strange with the assembly output from transmute.

pub unsafe fn foo(x: u8) -> bool {
    ::std::mem::transmute(x)
}

Playground

Expected Output

transmute should copy the input memory with no mutations. I expected this to put x in the return register and return:

playground::foo:
	movl	%edi, %eax
	retq

Actual Output

However the resulting value gets modified in the process producing the following output:

playground::foo:
	movl	%edi, %eax
	andb	$1, %al
	retq

Now, I don't have any issue with the compiler choosing any arbitrary approach for representing a bool within std::mem::size_of::<bool>() bytes. However, I was under the impression that std::mem::transmute never mutates the underlying data. To quote the documentation, "It’s equivalent to C’s memcpy under the hood, just like transmute_copy." At the moment it feels like this contract is not being upheld.

Structs

However, the bigger issue is transmute may or may not apply this adjustment to bool fields in structs. This could probably hide some nasty bugs that would be extremely hard to debug. Take for example the code below:

impl Bar {
    pub fn from_buffer(buffer: [u8; BAR_SIZE]) -> Self {
        unsafe { ::std::mem::transmute(buffer) }
    }

    pub fn to_buffer(self) -> [u8; BAR_SIZE] {
        unsafe { ::std::mem::transmute(self) }
    }
}

pub fn main() {
    let buffer: [u8; BAR_SIZE] = [0xFF; BAR_SIZE];
    let identity = Bar::from_buffer(buffer).to_buffer();
    assert_eq!(buffer, identity);
}

(Playground with panic (small Bar)) (Playground no panic (large Bar))
It is not possible to tell if this code will panic without seeing the contents of Bar. The primary factors that determine if a panic occurs seems to be the size of Bar and at when in the compilation process from_buffer/to_buffer are inlined. Small structs with less fields are more likely to have their bools adjusted. Depending on which function the transmute is in, the compiler may determine that the transmutes will cancel out and will not produce an error.

Meta

rustc --version --verbose:

jaspermeggitt@Jaspers-Laptop:~/tmp$ rustc --verbose --version
rustc 1.60.0 (7737e0b5c 2022-04-04)
binary: rustc
commit-hash: 7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c
commit-date: 2022-04-04
host: x86_64-unknown-linux-gnu
release: 1.60.0
LLVM version: 14.0.0
jaspermeggitt@Jaspers-Laptop:~/tmp$ rustc +nightly --verbose --version
rustc 1.62.0-nightly (8f36334ca 2022-04-06)
binary: rustc
commit-hash: 8f36334ca939a67cce3f37f24953ff6f2d3f3d33
commit-date: 2022-04-06
host: x86_64-unknown-linux-gnu
release: 1.62.0-nightly
LLVM version: 14.0.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-codegenArea: Code generation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions