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
Example Code
During some experimenting in the Rust playground, I noticed something strange with the assembly output from
transmute.Playground
Expected Output
transmuteshould copy the input memory with no mutations. I expected this to put x in the return register and return:Actual Output
However the resulting value gets modified in the process producing the following output:
Now, I don't have any issue with the compiler choosing any arbitrary approach for representing a
boolwithinstd::mem::size_of::<bool>()bytes. However, I was under the impression thatstd::mem::transmutenever mutates the underlying data. To quote the documentation, "It’s equivalent to C’smemcpyunder the hood, just liketransmute_copy." At the moment it feels like this contract is not being upheld.Structs
However, the bigger issue is
transmutemay or may not apply this adjustment toboolfields in structs. This could probably hide some nasty bugs that would be extremely hard to debug. Take for example the code below:(Playground with panic (small
Bar)) (Playground no panic (largeBar))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 ofBarand at when in the compilation processfrom_buffer/to_bufferare inlined. Smallstructswith less fields are more likely to have theirbools adjusted. Depending on which function thetransmuteis in, the compiler may determine that thetransmutes will cancel out and will not produce an error.Meta
rustc --version --verbose: