rustc and clang differ on noundef for hidden sret pointer parameters causing MSAN failures
Author: th0br0Created Sep 17, 2026Updated Sep 17, 2026
LabelsA-LLVMA-sanitizersC-bugA-ABI
Running the below code in msan (-Zsanitizer=memory) fails MSAN. A simple Cargo-based repro case can be found at https://github.com/th0br0/bug-rustc-cc-interop-msan.
Failure:
==3140423==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 ...
companion.cc:
struct Large {
uint64_t a[3];
};
extern "C" Large cpp_return_large(uint64_t x) {
return Large{x, 0, 0};
}
main.rs
#[allow(dead_code)]
pub enum Padded {
Small([u8; 1]),
Large([u32; 1]),
}
#[repr(C)]
pub struct Large(pub [u64; 3]);
unsafe extern "C" {
pub fn cpp_return_large(x: u64) -> Large;
}
#[inline(never)]
pub fn pass_padded(_: Padded) {}
fn main() {
pass_padded(Padded::Small([1]));
let large = unsafe { cpp_return_large(42) };
}
Automated root causing found that in rustc
https://github.com/rust-lang/rust/blob/main/compiler/rustc_target/src/callconv/mod.rs#L419 and https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_llvm/src/abi.rs#L108 emit noundef but clang does not https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/CGCall.cpp#L3085 this then leads to this LLM-generated step-by-step:
- Step A: Rust calls a function by value with an 8-byte enum containing padding bytes (e.g.
enum Padded { Small([u8; 1]), Large([u32; 1]) }, passed viaPassMode::Cast(i64)withoutnoundef), which stores non-zero padding shadow(0xffffff00_00000000)into__msan_param_tls[0]. - Step B: Rust calls a Clang-compiled
extern "C"function that returns a> 16-byte struct viasret (Arg#0, %rdi).Because rustc marks thesretargumentnoundef, LLVM'sMemorySanitizerPass::visitCallBaseskips writing0to__msan_param_tls[0]. - Step C: Because Clang omits
noundefon thesretparameter, MemorySanitizerPass in the C/C++ callee loads the stale shadow from__msan_param_tls[0]as the shadow for thesretpointer(%rdi)and traps when storing fields into*sret.
This supposedly can be fixed in either clang by emitting noundef or rust omitting noundef. I don't know which would be preferable.
Source: rust-lang/rust