<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/141361>141361</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
`va_arg` miscompiles on `x86_64`
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
folkertdev
</td>
</tr>
</table>
<pre>
The `good` function below works as expected, but the `bad` function miscompiles, and will read uninitialized memory.
https://godbolt.org/z/PWsMaYerv
```llvm
; Function Attrs: nounwind nonlazybind uwtable
define i64 @"bad"(ptr align 8 %0) unnamed_addr #3 {
start:
%1 = alloca ptr, align 8
store ptr %0, ptr %1, align 8
%2 = load ptr, ptr %1, align 8
%3 = va_arg ptr %2, i64
ret i64 %3
}
; Function Attrs: nounwind nonlazybind uwtable
define i64 @"good"(ptr align 8 %0) unnamed_addr #3 {
start:
%3 = va_arg ptr %0, i64
ret i64 %3
}
```
I don't see why storing and then loading a pointer, then using it, would cause the `va_arg` to be invalid.
---
<details open>
<summary>build/run instructions</summary>
<br>
```c
#include <stdarg.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
extern size_t good(va_list ap);
extern size_t bad(va_list ap);
int tester(size_t (*fn)(va_list), ...) {
size_t ret = 0;
va_list ap;
va_start(ap, fn);
ret = fn(ap);
va_end(ap);
return ret;
}
int main(int argc, char* argv[]) {
// assert(tester(good, 0x01LL, 0x02, 0x03LL) == 1);
assert(tester(bad, 0x01LL, 0x02, 0x03LL) == 1);
return 0;
}
```
Run this as
```
> clang --version
ClangBuiltLinux clang version 20.1.5 (https://github.com/llvm/llvm-project.git 7b09d7b446383b71b63d429b21ee45ba389c5134)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: ...
> clang bug.c bug.ll -o bug
> ./bug
> valgrind --track-origins=yes ./bug
```
</details>
the run will segfault, valgrind shows
```
==378134== Use of uninitialised value of size 8
==378134== at 0x1092BE: bad (in /tmp/bug)
==378134== by 0x1091D1: tester (in /tmp/bug)
==378134== by 0x109265: main (in /tmp/bug)
==378134== Uninitialised value was created by a stack allocation
==378134== at 0x1092A0: bad (in /tmp/bug)
==378134==
==378134== Invalid read of size 8
==378134== at 0x1092BE: bad (in /tmp/bug)
==378134== by 0x1091D1: tester (in /tmp/bug)
==378134== by 0x109265: main (in /tmp/bug)
==378134== Address 0x100000000000011 is not stack'd, malloc'd or (recently) free'd
==378134==
==378134==
==378134== Process terminating with default action of signal 11 (SIGSEGV)
==378134== General Protection Fault
==378134== at 0x1092BE: bad (in /tmp/bug)
==378134== by 0x1091D1: tester (in /tmp/bug)
==378134== by 0x109265: main (in /tmp/bug)
```
>From what I can tell, the offending code is
```asm
mov rcx, qword ptr [rsp - 32]
mov edx, dword ptr [rsp - 12]
mov rsi, qword ptr [rcx + 16]
```
Nothing useful is stored at `rsp - 32`, it is uninitialized memory!
---
Probably this mostly went unnoticed because clang uses its own implementation of `va_arg`, and seemingly never uses this LLVM intrinsic.
This issue came up in the context of https://github.com/rust-lang/rust/issues/44930#issuecomment-2902519131, where we're trying to improve the API for C variadic functions in rust. Apparently so far the optimizer/inliner have made sure invalid patterns did not occur, but that is unreliable.
this is an issue that seems similar, but has less information https://github.com/llvm/llvm-project/issues/61021.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzcWEuPG7kR_jXUpSCBzW69DjpoZqyFAW9gZL0b5DRgN0stxmxSIdl6-NcHRbZkaSxvYCeXRBiMyOLHYr1YVZQMQbcWccWmT2z6MpJ93Dm_2jrzGX1UeBjVTp1Xn3YIbMZb5xSbcdj2tonaWajRuCMcnf8cQAbA0x6biIqJZ6j7CDFvq-X9rk6HxnV7bTAQUloFR20MeJQKequtjloa_QUVdNg5f54wvmZ8vYtxH1i5ZmLDxKZ1qnYmTpxvmdh8YWLz8W_hV_l39IcMZzOe_4w5dDQvn2BzkWEdoydeYF1vj9oqsM4a-eVc07g_RlkbZHytcKstgp5VwCrOhCBlhGBisY8epNGthQUwMeVMLKG3VnaoXqVSHpgoS2DzJ8bXIUofSXK-BgIXwMoXkMa4RsI--mSGzCtBQnQeaWHg_HwZF2-RTExFYmacVBdWfwIuE_ggX6VvLzhBOD2rCOMxZmXFtCSbzV8GY_5XjJcC6D-23iMd-I0O31HiEg5ZofegnGViHiEgwnF3TkbXtk3xGHdok0kTAfZO24jJtmmlD0TXkQhH1xsFjewDXgI-i0YxHx3UCNoepNFqCOPxeHyx6bPCKLUJ4PZoWfkuE0PfddKfWfmu7rVRTGx8b0HbEH2fHBBY-czE5isu76v9MLwP_4YmotS2Mb1CoAOikr6d7Ab43ZoMAX18vBai0vb7a0bX39_nrkt8jaeI3kLQX_A1Qg6KxUG-Gh0iyD0TS1Y-fYNLN-8RjPG1thEihuSjxYBnYsHEemsJd92XJs8wmUwo4nJ8AcDlDIocCi6eOcPNaeUFepCvOSLFgoR4hnzEdf3Cg8iLGzGHvWjVG7rH2HtLX4M-l0tHWnVSEx8aSt82dF6zk56JNc0POW_f65LzI2RPMrG4GiZb-hn4iRcfPgwjMXyXRFmS5CR8cSf2t6ySM36Q01f7kLr8Ttn7y_nX3kLcaSoqb4I5Rfo7aIy0LYzHB_RBO8v4-pkoT7028YO2_WlADOsg-KSYTCkk3hQRHXd9PWlcx8Qm1Yn8Nd579w9s4qTVEeY1X6p5XVWzclHW86KelaoSy1oUiNW0luVi2UyLsiJF-fqT9C15cg2nxex1Vo17-9m6ox0bkmvc2p5Au1TsOqfQEHTvgj5RVrIhSmNQvWhPdIrTW43rvp006b8xMHY0GtYnTGy-zg7StJ6S8XgcvWw-j53XrabE8XLGcAu-N3xOLENWut5XSmuUglKVDthuZW9S7rseE3bu-NBVFALlfEHWyeHwe0Bw25syH1ARnz6R6RqmYvVgp4zATwVfiqd3ZJpaKkgXg-I9dvtBpeSDB7sBoD5nBsVLQQxyLP8kDzGbEg-6nT_C4fcHah9lgMajjKiIvYQQZfN56A5iju7HwlwtsuY_YZHH1Pe5VuVm7N945FaE_2GnwFopjyEkJjefogAdwLqYPcLEPOW8LnmGZuCSpB4btNGcKeltPWIC_oDJH1M_eteQUBF9p62M1HEcddyBwnT_QOZmLPmotdJAUZA0v73_5bd3v_zxfW1_QYteGjogYuaxSRf6_8fH90lt410Hx52M8B4aaSGiMUMrB267RZu6vMYpJH-_yWIy0NuhcwfwzYl2_fPovMpt5_TJhz2MoRRUgzOKPqgSUn2LLG6QPuhv-TUnYOIJilnGvdHkLy7uSNY-4LY3JG16KShyEZvxqzSz3A5HQjx6UDFRwJtm9KN3tazNOdfdzoVoznBEG6ktd1E3lJ0wt7m5FvUBA-gYwB0t6G5vsEMb5SUob_vgyzMvIHbatuYMFg_oM4t04IcPf_wK2kavbdDNZJDuEy3pEHqERnYI_R60TX5rnI14inTQn1R034c4JmGHMRObxC0wsamqZcmpTyVC4zoSfiyWXEyLZVGmt9Nxhx7hSBfaI0R_JttHR8p6d8jt_vrje9g6D1QMvZZKN9eHbiBh6dgJrPd76VOSgOBgK30Ovn3Unf5CDdVGW6MtetjJA0InFULo_fXpAHsZqRcOoLRKKck1Te-_PrTl4GuPRtPra3Kp3MmAIO1gxoQkNwQIutNGXnnsZABDCUfbrfNd9uOPdUu35p0VXBQkxUitSrUsl3KEq2JeLQo-41U12q2qpSwW82JW1Nt5vdw25bKui2bKy6bmqOr5SK8EF1M-FVUx57yqJnxWSi7LLedlvVgWBas4dlKbCQkxcb4dpeNXRVWUs2JkZI0mpJ82hLB4zCag9-f0ZeRXSfK6bwOrODX54SubqKPB1d1L7uZHC3CWoju3d2zGR703q5-31CDtYSX-FQAA___aOi-N">