Issue 141361
Summary `va_arg` miscompiles on `x86_64`
Labels new issue
Assignees
Reporter folkertdev
    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.

_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to