Closed
Description
I tried this code:
// On a target that uses cast ABI, e.g. --target=aarch64-unknown-linux-gnu
#![crate_type = "lib"]
#[repr(C)]
pub struct TwoU16s {
one: u16,
two: u16,
}
extern "C" {
pub fn rust_dbg_extern_identity_TwoU16s(v: TwoU16s) -> TwoU16s;
}
#[no_mangle]
pub unsafe fn foo() {
let x = TwoU16s { one: 0, two: 0 };
rust_dbg_extern_identity_TwoU16s(x);
}
I expected to see this happen: No UB.
Instead, this happened: https://godbolt.org/z/bd8MT4bEM
define void @foo() {
start:
%0 = alloca i64, align 8
%1 = alloca { i16, i16 }, align 2
%2 = alloca { i16, i16 }, align 2
%3 = getelementptr inbounds { i16, i16 }, ptr %2, i32 0, i32 0
store i16 0, ptr %3, align 2
%4 = getelementptr inbounds { i16, i16 }, ptr %2, i32 0, i32 1
store i16 0, ptr %4, align 2
%5 = load i64, ptr %2, align 2
%6 = call i64 @rust_dbg_extern_identity_TwoU16s(i64 %5)
call void @llvm.lifetime.start.p0(i64 4, ptr %1)
call void @llvm.lifetime.start.p0(i64 4, ptr %0)
store i64 %6, ptr %0, align 8
call void @llvm.memcpy.p0.p0.i64(ptr align 2 %1, ptr align 8 %0, i64 4, i1 false)
call void @llvm.lifetime.end.p0(i64 4, ptr %0)
%7 = getelementptr inbounds { i16, i16 }, ptr %1, i32 0, i32 0
%_1.0 = load i16, ptr %7, align 2, !noundef !2
%8 = getelementptr inbounds { i16, i16 }, ptr %1, i32 0, i32 1
%_1.1 = load i16, ptr %8, align 2, !noundef !2
call void @llvm.lifetime.end.p0(i64 4, ptr %1)
ret void
}
There are two problems here, first:
%2 = alloca { i16, i16 }, align 2
...
%5 = load i64, ptr %2, align 2
...we load 8 bytes from a 4 byte alloca. Alive2 believes this is UB: https://alive2.llvm.org/ce/z/eBaekz.
Second:
call void @llvm.lifetime.start.p0(i64 4, ptr %0)
store i64 %6, ptr %0, align 8
call void @llvm.lifetime.end.p0(i64 4, ptr %0)
...only 4 bytes of %0
are marked live, but we access 8 bytes. The langref is not clear about what the size arguments of lifetime intrinsics actually do, but this is probably UB. (Alive2 ignores the size arguments, so it can't detect this.)
@rustbot label I-unsound A-ffi
This was uncovered via #122053 (comment), where this issue is the root cause of a miscompilation. That comment contains a bit more context.
Meta
rustc --version --verbose
:
rustc 1.76.0 (07dca489a 2024-02-04)
binary: rustc
commit-hash: 07dca489ac2d933c78d3c5158e3f43beefeb02ce
commit-date: 2024-02-04
host: x86_64-unknown-linux-gnu
release: 1.76.0
LLVM version: 17.0.6
(also reproducible on very recent master 766bdce)