Skip to content

[RISCV] musttail with indirect arguments causes use-after-free #185089

@xroche

Description

@xroche

On RV32, musttail calls where arguments are passed indirectly (CCValAssign::Indirect) produce dangling pointers. The caller loads data from the incoming indirect pointer, creates a new stack temporary, stores the data there, and passes a pointer to the new slot. The tail call then deallocates the caller's stack frame before the callee executes, leaving the pointer dangling.

Example: fp128 on RV32 is passed indirectly (16 bytes, pointer in a0). i128 on RV32 also triggers this path.

declare i32 @callee(fp128 %a)

define i32 @caller(fp128 %a) {
entry:
  %v = musttail call i32 @callee(fp128 %a)
  ret i32 %v
}

On RV32, the generated code:

  1. Loads fp128 data from incoming pointer (caller's caller's frame)
  2. Creates a new stack slot via DAG.CreateStackTemporary
  3. Stores data to the new stack slot
  4. Passes pointer to new stack slot in a0
  5. Tail-jumps to callee -- stack frame is deallocated, pointer is now dangling

The correct behavior for musttail is to forward the original incoming pointer directly, since musttail guarantees matching prototypes. The incoming pointer points to the caller's caller's frame, which remains valid after the tail call.

Note: PR #184972 fixed the non-musttail case by rejecting tail calls with indirect args entirely. This issue is specifically about musttail, which must be a tail call and therefore needs a different fix (forwarding the pointer rather than rejecting the optimization).

On RV64, fp128 and i128 fit in 2 registers (direct), so this issue only manifests on RV32 (or with types larger than 2xXLEN on RV64).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions