llvm / llvm/llvm-project

Miscompiled address of stack variable in function marked preserve_all when -fno-omit-frame-pointer -mavx

Open
#210,756 2 comments 0 reactions 1 assignee Claimed by @AayushMainali-Github View on GitHub
backend:X86 miscompilation
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

repro (`clang++ -O2 -std=c++23 -fno-omit-frame-pointer -mavx` on an x86-64 machine):

```c++
// slow() is [[clang::preserve_all]]. With AVX enabled that convention makes it save ymm0-ymm15,
// so its frame is realigned to 32 bytes (`and rsp, -32`) and its locals are addressed relative
// to RSP. It then calls combine(), whose trailing arguments are passed on the stack. The backend
// X86CallFrameOptimization pass turns those argument stores into `push`es (adjusting RSP by 8
// first for alignment), but a `lea` computing the address of the local `suffix` *inside* that
// push window is not rebased for the RSP adjustment. combine() therefore receives `&suffix - 8`
// and reads the neighbouring 8 bytes as suffix._M_len -> a garbage length (a pointer value).
//
// The bug disappears if ANY ONE of these is changed:
// * remove -mavx (no ymm saves -> no 32-byte realignment -> RBP-relative frame)
// * remove -fno-omit-frame-pointer
// * remove [[clang::preserve_all]]
// * add -mllvm -no-x86-call-frame-opt (disables the buggy pass)
#include
#include
#include
#include

[[gnu::noinline]] std::string combine(std::string_view n1, std::string_view n2, const std::string& a, long f1, long f2, const std::string& suffix) {
if (suffix.size() != 22) {
std::printf("MISCOMPILED: suffix.size()=%zu suffix.data()=%p\n", suffix.size(), (const void*)suffix.data());
std::exit(1);
}
return std::string(n1) + std::string(n2) + a + std::to_string(f1) + std::to_string(f2) + suffix;
}

std::string g_a; // a global just so the compiler can't constant-fold `a` away

[[clang::preserve_all]] [[gnu::noinline]] std::string slow() {
std::string suffix("payload-2026-07-17-abc"); // 22 chars; correct size is 22
// `suffix` is a local in slow()'s (32-byte-realigned, RSP-addressed) frame; its address is
// one of the stack arguments, and that is the `lea` the backend fails to rebase.
return combine("n1", "n2", g_a, 111L, 222L, suffix);
}

int main() {
g_a = "some-value-here";
std::string r = slow();
std::printf("OK %s\n", r.c_str());
}
```

[godbolt](https://preserve_all.godbolt.org/z/hsa9zGrWb)
seems to reproduce since clang 17

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.