| Issue |
208946
|
| Summary |
[Xtensa] Incoming stack arguments read at the wrong address when the frame is dynamically realigned (windowed ABI)
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
rnortman
|
## Summary
When a windowed-ABI function has an over-aligned stack object (alignment > 32,
forcing dynamic SP realignment) *and* takes arguments passed on the stack, the
generated code reads those arguments at the wrong address. The realignment
moves SP by a runtime-variable pad, but the incoming-argument frame indices are
still resolved as `SP + framesize + k`, which is only correct against the SP
value at function entry. Every stack-argument access is off by the pad
(1–64 bytes for a 64-byte-aligned object).
Neither condition alone miscompiles: with realignment only, all arguments stay
in registers; with stack arguments only, SP is never moved.
Confirmed on current main (31ddc7e56481). Also present in LLVM 20 and 21 (the
espressif/MabezDev forks used by the ESP32 toolchains reproduce identically).
I'll be pushing a PR with the fix right after filing this.
## Reproducer
```llvm
target triple = "xtensa-unknown-none-elf"
declare void @sink(ptr)
define i32 @victim(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f,
ptr %g, ptr %h, ptr %i) {
%buf = alloca [64 x i8], align 64 ; forces dynamic SP realignment
call void @sink(ptr %buf)
store i8 0, ptr %g ; %g/%h/%i are passed on the stack
store i8 0, ptr %h
store i8 0, ptr %i
call void @sink(ptr %buf)
ret i32 %a
}
```
```
llc -mtriple=xtensa -mattr=+windowed -O2 < repro.ll
```
Output (annotated):
```asm
victim:
entry a1, 160 # SP = entry_SP - 160
movi a8, 63
movi a9, 64
and a8, a1, a8
sub a8, a9, a8
add a1, a1, a8 # realign: SP += pad, pad in [1,64]
...
l32i a8, a1, 160 # %g: reads *(SP + 160) = entry_SP + pad WRONG
s8i a9, a8, 0 # should be entry_SP + 0
l32i a8, a1, 164 # %h: entry_SP + pad + 4 WRONG
s8i a9, a8, 0
l32i a8, a1, 168 # %i: entry_SP + pad + 8 WRONG
s8i a9, a8, 0
```
`(entry_SP - 160) + pad + 160 = entry_SP + pad`, but the caller placed `%g` at
`entry_SP + 0`. The loaded "pointer" is garbage from the caller's frame;
dereferencing it corrupts memory or traps.
Under the call8 convention the first six arguments are in a2–a7, so any
function with ≥7 arguments (or fewer, larger ones) plus an over-aligned local
hits this.
## Analysis
`XtensaFrameLowering::emitPrologue` realigns SP in place
(`AND`/`SUB`/`ADD a1, a1, pad`) when `MaxAlignment > 32`, but nothing preserves
the entry SP, and `XtensaRegisterInfo::eliminateFrameIndex` resolves fixed
(incoming-argument) frame objects against the moved SP with offsets computed
against the entry SP. Targets that support stack realignment normally keep a
frame or base pointer for exactly this case; the Xtensa backend has neither on
this path.
## Second defect on the same path: pad computed from an argument register when the function has dynamic allocas
I know you'd usually file these separately, but the bugs are on the same path
and fixed in the same PR.
The realignment sequence computes the pad from `getFrameRegister(MF)`:
```cpp
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::AND))
.addReg(RegMisAlign, RegState::Define)
.addReg(FP) // A7 when hasFP
.addReg(RegMisAlign);
```
When the function also has dynamic allocas, `getFrameRegister` is A7 — but the
`AND` executes before A7 is set up as the frame pointer, while it still holds
the sixth incoming argument. The frame is "realigned" by an amount derived
from an argument value, so the over-aligned object's alignment guarantee is
silently broken:
```asm
realign_alloca:
entry a1, 160
movi a8, 63
...
and a8, a7, a8 # a7 = incoming arg, not a frame register
sub a8, a9, a8
add a1, a1, a8 # SP moved by garbage
or a7, a1, a1 # only NOW does a7 become FP
```
(Reproducer: add `%dyn = alloca i8, i32 %n` to `victim` above.)
## Real-world impact
Found via a Rust firmware crash on ESP32-S3 (esp toolchain, which carries this
backend): a function with a 64-byte-aligned DMA buffer and 9 parameters
dereferenced a displaced argument pointer and died with LoadStoreError. Any
windowed-ABI code combining `#[repr(align(64))]`/`alignas(64)` stack objects
with stack-passed arguments is affected.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs