dsecurity49 opened PR #14235 from dsecurity49:fix-apple-aarch64-stack-slot-overflow to bytecodealliance:main:
Problem
Fixes #13973
When a stack-spilled argument has a
sextoruextextension under theapple_aarch64calling convention,get_ext_modereturns the extension, which causes:
- The caller to sign/zero-extend the value to a full 8-byte word before storing it to the stack slot.
- The callee to load a full 8-byte word from the stack slot.
However, the slot-size condition used an
||that allowed sub-word slots (e.g. 2 bytes fori16 sext) wheneverargs_or_rets == Args, regardless of whether an extension was present. This meant the caller wrote 8 bytes into a 2-byte slot, silently corrupting the 6 adjacent stack bytes.Reproduction
The included
repro_i16.clifdemonstrates the issue. Before this fix:FAIL ./repro_i16.clif: run Failed test: run: %caller() == 1234605616436508552, actual: 1234605619298697215After this fix:
1 tests <- passFix
Remove the
args_or_rets == ArgsOrRets::Argsbranch from the condition. Sub-word slots are only safe whenparam.extension == ArgumentExtension::None, because only then will the caller store exactlysizebytes and the callee load exactlysizebytes.-let size = if (is_apple_cc || is_winch_return) - && (args_or_rets == ArgsOrRets::Args - || param.extension == ir::ArgumentExtension::None) +let size = if (is_apple_cc || is_winch_return) + && param.extension == ir::ArgumentExtension::NoneTested natively on AArch64 Linux (Android/Termux, Qualcomm TRINKET).
dsecurity49 requested cfallin for a review on PR #14235.
dsecurity49 requested wasmtime-compiler-reviewers for a review on PR #14235.
dsecurity49 requested alexcrichton for a review on PR #14235.
dsecurity49 requested wasmtime-default-reviewers for a review on PR #14235.
github-actions[bot] added the label cranelift on PR #14235.
github-actions[bot] added the label cranelift:area:aarch64 on PR #14235.
dsecurity49 edited PR #14235:
Fixes #13973
For
apple_aarch64,get_ext_modereturns specified extensions (sext/uext), which causes caller/callee to load and store full 64-bit words for stack arguments. The slot size condition previously allowed sub-word sizes for spilled arguments wheneverargs_or_rets == Args, causing 8-byte stores to overwrite adjacent stack slots.This updates the check to only allow sub-word stack slots when
param.extension == None.
cfallin commented on PR #14235:
If I understand this right, you are asserting that the Apple aarch64 convention actually does 8-align args just because they are extended to 64 bits (in registers). That doesn't sound right to me but I'm happy to be corrected; could you link the documentation you're using as a reference for this change? Thanks!
(If the extension only applies to in-register values instead, as I suspect, then the correct fix is instead to do narrow (true-width) loads/stores when values live on the stack, I think.)
dsecurity49 commented on PR #14235:
You are absolutely correct. The Apple ABI dictates that stack arguments smaller than 8 bytes occupy only their natural size (e.g., 2 bytes for an
i16). The extension to 64 bits only applies to arguments passed in registers.Because
get_ext_modeincranelift/codegen/src/isa/aarch64/abi.rscurrently returnsspecifiedforAppleAarch64regardless of the argument's location (register vs. stack), the generic code inmachinst/abi.rsforces a 64-bit load/store for stack slots when an extension is specified.My previous fix just forced the stack slot to be 8 bytes to absorb the 8-byte store generated by
machinst/abi.rs. But as you pointed out, the true fix is to do a narrow (true-width) load/store when the value lives on the stack.Since
M::get_ext_mode(call_conv, extension)doesn't currently receive context about whether the argument is aRegorStackslot, what is the preferred way to handle this in Cranelift?
- Should we pass a location flag to
get_ext_modeso it can returnArgumentExtension::Nonefor stack slots onAppleAarch64?- Or should the generic code in
machinst/abi.rsjust ignoreextwhen loading/storing from aStackslot on little-endian platforms (though the comment at line 1591 mentions it is needed for big-endian)?Happy to update the PR with whichever architectural approach you prefer!
dsecurity49 deleted a comment on PR #14235:
You are absolutely correct. The Apple ABI dictates that stack arguments smaller than 8 bytes occupy only their natural size (e.g., 2 bytes for an
i16). The extension to 64 bits only applies to arguments passed in registers.Because
get_ext_modeincranelift/codegen/src/isa/aarch64/abi.rscurrently returnsspecifiedforAppleAarch64regardless of the argument's location (register vs. stack), the generic code inmachinst/abi.rsforces a 64-bit load/store for stack slots when an extension is specified.My previous fix just forced the stack slot to be 8 bytes to absorb the 8-byte store generated by
machinst/abi.rs. But as you pointed out, the true fix is to do a narrow (true-width) load/store when the value lives on the stack.Since
M::get_ext_mode(call_conv, extension)doesn't currently receive context about whether the argument is aRegorStackslot, what is the preferred way to handle this in Cranelift?
- Should we pass a location flag to
get_ext_modeso it can returnArgumentExtension::Nonefor stack slots onAppleAarch64?- Or should the generic code in
machinst/abi.rsjust ignoreextwhen loading/storing from aStackslot on little-endian platforms (though the comment at line 1591 mentions it is needed for big-endian)?Happy to update the PR with whichever architectural approach you prefer!
dsecurity49 requested wasmtime-compiler-s390x-reviewers for a review on PR #14235.
dsecurity49 updated PR #14235.
dsecurity49 updated PR #14235.
dsecurity49 updated PR #14235.
dsecurity49 commented on PR #14235:
you're right. The extension should only apply to registers. get_ext_mode didn't know the location though, so it was forcing 8-byte loads/stores for anything extended.
I've updated the PR to thread an ABIArgLocation into get_ext_mode so we can correctly return None for stack args on apple AArch64. I also added a precise-output test for it.
github-actions[bot] added the label cranelift:area:machinst on PR #14235.
github-actions[bot] added the label cranelift:area:x64 on PR #14235.
alexcrichton unassigned alexcrichton from PR #14235 aarch64: fix apple_aarch64 stack slot size for sext/uext spilled args.
cfallin commented on PR #14235:
Thanks! I'll repeat a question from earlier:
could you link the documentation you're using as a reference for this change? Thanks!
Both for my own use and ideally linked in a comment so we can have a canonical source going forward.
dsecurity49 updated PR #14235.
dsecurity49 commented on PR #14235:
Source: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
:thumbs_up: cfallin submitted PR review:
Thanks!
cfallin added PR #14235 aarch64: fix apple_aarch64 stack slot size for sext/uext spilled args to the merge queue.
:check: cfallin merged PR #14235.
cfallin removed PR #14235 aarch64: fix apple_aarch64 stack slot size for sext/uext spilled args from the merge queue.
Last updated: Sep 20 2026 at 18:08 UTC