[llvm] 181d808 - [AArch64][PAuth] Fix return-address auth for swifttailcc with FPDiff > 0 (#203340)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 14:07:05 PDT 2026


Author: Jon Roelofs
Date: 2026-06-12T14:07:01-07:00
New Revision: 181d8084770935e4e3cd1877d317e95b73fe8368

URL: https://github.com/llvm/llvm-project/commit/181d8084770935e4e3cd1877d317e95b73fe8368
DIFF: https://github.com/llvm/llvm-project/commit/181d8084770935e4e3cd1877d317e95b73fe8368.diff

LOG: [AArch64][PAuth] Fix return-address auth for swifttailcc with FPDiff > 0 (#203340)

When a swifttailcc tail call has FPDiff > 0 (the caller received more
stack argument space than the callee pops), the epilogue contains an SP
adjustment to discard the leftover argument space. The existing code
treated both FPDiff < 0 and FPDiff > 0 uniformly in a single 'FPDiff !=
0' block, using AUTI[AB]1716 with a reconstructed entry-SP in x16 for
both cases.

For FPDiff < 0 (callee pops more) that reconstruction is necessary and
correct. For FPDiff > 0 it is wrong: by the time we enter the block the
post-index LDP has already adjusted SP back to the frame base, but the
'add sp, sp, #N' argument pop has not yet run. Entry SP equals the
current SP at that point, so AUTI[AB]SP would work directly, but instead
the combined block bumped SP via StackOffset::getFixed(-FPDiff) which
overshoots, and then emits AUTIA1716 with a wrong discriminator. Worse
yet, the SP restore had already been emitted *before* the auth, leaving
the live argument stack below SP and outside the red-zone during the
authentication window.

Fix by splitting the block on the sign of ArgumentStackToRestore:

* `< 0`: reconstruct entry SP in x16, save LR in x17, authenticate with
AUTI[AB]1716 (or AUTI[AB]171615 / PACM+AUTI[AB]1716 for PAuthLR).

* `> 0`: temporarily remove the 'add sp, sp, #N' SP-modifying
instructions from before the auth instruction (SPMods shuffle),
authenticate with AUTI[AB]SP (SP == entry SP at this point), then
re-insert the SP adjustment afterward.

Added: 
    llvm/test/CodeGen/AArch64/swifttail-ptrauth.ll

Modified: 
    llvm/lib/Target/AArch64/AArch64PointerAuth.cpp
    llvm/test/CodeGen/AArch64/arm64e-tail-call-autib.ll
    llvm/test/CodeGen/AArch64/pauth-lr-tail-call-fpdiff.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp b/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp
index 3c0ed248fc28a..75c354cbba18b 100644
--- a/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp
+++ b/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp
@@ -189,21 +189,24 @@ void AArch64PointerAuthImpl::authenticateLR(
     return;
   }
 
-  // When FPDiff != 0 (tail call with callee-popped stack arg space), SP has
-  // been adjusted and no longer matches the entry SP used as the signing
-  // modifier. We must reconstruct entry SP for authentication.
   auto &AFL = *static_cast<const AArch64FrameLowering *>(
       MF.getSubtarget().getFrameLowering());
-  if (int64_t FPDiff = AFL.getArgumentStackToRestore(MF, MBB)) {
-    // Use AUTI[AB]1716 variants: x17=LR, x16=entry_SP.
+  int64_t ArgumentStackToRestore = AFL.getArgumentStackToRestore(MF, MBB);
+
+  // When ArgumentStackToRestore < 0, the tail callee pops more argument space
+  // than this function received, so after the frame teardown SP is below the
+  // entry SP used as the signing modifier. Reconstruct entry SP in x16 and
+  // authenticate using AUTI[AB]1716 (x17=LR, x16=entry_SP).
+  if (ArgumentStackToRestore < 0) {
+    emitFrameOffset(MBB, MBBI, DL, AArch64::X16, AArch64::SP,
+                    StackOffset::getFixed(-ArgumentStackToRestore), TII,
+                    MachineInstr::FrameDestroy);
+
     BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXrs), AArch64::X17)
         .addReg(AArch64::XZR)
         .addReg(AArch64::LR)
         .addImm(0)
         .setMIFlag(MachineInstr::FrameDestroy);
-    emitFrameOffset(MBB, MBBI, DL, AArch64::X16, AArch64::SP,
-                    StackOffset::getFixed(-FPDiff), TII,
-                    MachineInstr::FrameDestroy);
 
     if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
       assert(PACSym && "No PAC instruction to refer to");
@@ -244,6 +247,29 @@ void AArch64PointerAuthImpl::authenticateLR(
     return;
   }
 
+  // When ArgumentStackToRestore > 0, this function received more argument
+  // space than the tail callee pops. The epilogue contains an SP adjustment
+  // (e.g. "add sp, sp, #N") to discard the leftover argument space. We must
+  // authenticate *before* that adjustment so that AUTI[AB]SP sees the entry
+  // SP discriminator. Move any such SP-adjusting instructions to after the
+  // authentication instruction.
+  //
+  // We cannot simply bump SP first and then use AUTI[AB]SP with the bumped
+  // value, because the live arguments would fall below SP and potentially
+  // outside the red-zone.
+  SmallVector<MachineInstr *, 2> SPMods;
+  if (ArgumentStackToRestore > 0) {
+    for (auto I = MBBI; I->getFlag(MachineInstr::FrameDestroy); --I) {
+      if ((I->getOpcode() == AArch64::ADDXri ||
+           I->getOpcode() == AArch64::SUBXri) &&
+          I->getOperand(0).getReg() == AArch64::SP &&
+          I->getOperand(1).getReg() == AArch64::SP)
+        SPMods.push_back(&*I);
+    }
+  }
+  for (auto *MI : SPMods)
+    MI->removeFromParent();
+
   if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
     assert(PACSym && "No PAC instruction to refer to");
     emitPACCFI(MBB, MBBI, MachineInstr::FrameDestroy, EmitAsyncCFI);
@@ -254,6 +280,7 @@ void AArch64PointerAuthImpl::authenticateLR(
   } else {
     if (MFnI->branchProtectionPAuthLR()) {
       emitPACSymOffsetIntoReg(*TII, MBB, MBBI, DL, PACSym, AArch64::X16);
+
       BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACM))
           .setMIFlag(MachineInstr::FrameDestroy);
       emitPACCFI(MBB, MBBI, MachineInstr::FrameDestroy, EmitAsyncCFI);
@@ -269,6 +296,9 @@ void AArch64PointerAuthImpl::authenticateLR(
     BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
         .setMIFlag(MachineInstr::FrameDestroy);
   }
+
+  for (auto *MI : SPMods)
+    MBB.insert(MBBI, MI);
 }
 
 unsigned llvm::AArch64PAuth::getCheckerSizeInBytes(AuthCheckMethod Method) {

diff  --git a/llvm/test/CodeGen/AArch64/arm64e-tail-call-autib.ll b/llvm/test/CodeGen/AArch64/arm64e-tail-call-autib.ll
index 707bf229c3787..d8e22b0c3cb04 100644
--- a/llvm/test/CodeGen/AArch64/arm64e-tail-call-autib.ll
+++ b/llvm/test/CodeGen/AArch64/arm64e-tail-call-autib.ll
@@ -38,8 +38,8 @@ define swifttailcc void @test_async_tail_call(ptr swiftasync %ctx) "ptrauth-retu
 ; CHECK-NEXT:    ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
 ; CHECK-NEXT:    and x29, x29, #0xefffffffffffffff
 ; CHECK-NEXT:    add sp, sp, #32
-; CHECK-NEXT:    mov x17, x30
 ; CHECK-NEXT:    add x16, sp, #16
+; CHECK-NEXT:    mov x17, x30
 ; CHECK-NEXT:    autib1716
 ; CHECK-NEXT:    mov x30, x17
 ; CHECK-NEXT:    eor x16, x30, x30, lsl #1

diff  --git a/llvm/test/CodeGen/AArch64/pauth-lr-tail-call-fp
diff .ll b/llvm/test/CodeGen/AArch64/pauth-lr-tail-call-fp
diff .ll
index f26ff897cf352..67bff0b15fd49 100644
--- a/llvm/test/CodeGen/AArch64/pauth-lr-tail-call-fp
diff .ll
+++ b/llvm/test/CodeGen/AArch64/pauth-lr-tail-call-fp
diff .ll
@@ -60,8 +60,8 @@ define swifttailcc void @tail_call_fp
diff _a_key(ptr swiftasync %ctx) "branch-pro
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-NEXT:    .cfi_restore w30
 ; CHECK-NEXT:    .cfi_restore w29
-; CHECK-NEXT:    mov x17, x30
 ; CHECK-NEXT:    add x16, sp, #16
+; CHECK-NEXT:    mov x17, x30
 
 ; COMPAT-NEXT:   adrp x15, .Ltmp0
 ; COMPAT-NEXT:   add x15, x15, :lo12:.Ltmp0
@@ -138,8 +138,8 @@ define swifttailcc void @tail_call_fp
diff _b_key(ptr swiftasync %ctx) "branch-pro
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-NEXT:    .cfi_restore w30
 ; CHECK-NEXT:    .cfi_restore w29
-; CHECK-NEXT:    mov x17, x30
 ; CHECK-NEXT:    add x16, sp, #16
+; CHECK-NEXT:    mov x17, x30
 
 ; COMPAT-NEXT:   adrp x15, .Ltmp1
 ; COMPAT-NEXT:   add x15, x15, :lo12:.Ltmp1
@@ -241,7 +241,7 @@ define swifttailcc void @tail_call_no_fp
diff _b_key(ptr swiftasync %ctx) "branch-
 ; COMPAT-NEXT:   hint #27
 
 ; V83A-NEXT:     hint #39
-; V83A-NEXT:      .cfi_negate_ra_state_with_pc
+; V83A-NEXT:     .cfi_negate_ra_state_with_pc
 ; V83A-NEXT:   .Ltmp3:
 ; V83A-NEXT:     pacibsp
 
@@ -341,8 +341,8 @@ define swifttailcc void @indirect_tail_call_fp
diff _a_key(ptr swiftasync %ctx, pt
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-NEXT:    .cfi_restore w30
 ; CHECK-NEXT:    .cfi_restore w29
-; CHECK-NEXT:    mov x17, x30
 ; CHECK-NEXT:    add x16, sp, #16
+; CHECK-NEXT:    mov x17, x30
 
 ; COMPAT-NEXT:   adrp x15, .Ltmp4
 ; COMPAT-NEXT:   add x15, x15, :lo12:.Ltmp4
@@ -420,8 +420,8 @@ define swifttailcc void @indirect_tail_call_fp
diff _b_key(ptr swiftasync %ctx, pt
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-NEXT:    .cfi_restore w30
 ; CHECK-NEXT:    .cfi_restore w29
-; CHECK-NEXT:    mov x17, x30
 ; CHECK-NEXT:    add x16, sp, #16
+; CHECK-NEXT:    mov x17, x30
 
 ; COMPAT-NEXT:   adrp x15, .Ltmp5
 ; COMPAT-NEXT:   add x15, x15, :lo12:.Ltmp5
@@ -523,7 +523,7 @@ define swifttailcc void @indirect_tail_call_no_fp
diff _b_key(ptr swiftasync %ctx,
 ; COMPAT-NEXT:   hint #27
 
 ; V83A-NEXT:     hint #39
-; V83A-NEXT:      .cfi_negate_ra_state_with_pc
+; V83A-NEXT:     .cfi_negate_ra_state_with_pc
 ; V83A-NEXT:   .Ltmp7:
 ; V83A-NEXT:     pacibsp
 

diff  --git a/llvm/test/CodeGen/AArch64/swifttail-ptrauth.ll b/llvm/test/CodeGen/AArch64/swifttail-ptrauth.ll
new file mode 100644
index 0000000000000..5d31d7a99ef7b
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/swifttail-ptrauth.ll
@@ -0,0 +1,202 @@
+; RUN: llc -mtriple=aarch64              < %s | FileCheck --check-prefixes=CHECK,COMPAT %s
+; RUN: llc -mtriple=aarch64 -mattr=v8.3a < %s | FileCheck --check-prefixes=CHECK,V83A %s
+; RUN: llc -mtriple=aarch64 -mattr=v9a -mattr=pauth-lr < %s | FileCheck --check-prefixes=CHECK,V9A %s
+; RUN: sed 's/"branch-protection-pauth-lr" //g' %s \
+; RUN:   | llc -mtriple=aarch64 -mattr=v9a \
+; RUN:   | FileCheck --check-prefixes=CHECK,PAUTH %s
+
+; These tests cover the interaction between swifttailcc and return-address
+; signing. The key invariant is that AUTIASP/AUTIBSP uses the current SP as the
+; discriminator, which must equal the entry SP at the point of signing. When a
+; tail call involves a stack-argument size mismatch (FPDiff != 0) this invariant
+; can be violated if we're not careful.
+
+declare swifttailcc void @callee_stack0()
+declare swifttailcc void @callee_stack8([8 x i64], i64)
+
+; FPDiff == 0: SP is unchanged at epilogue. autiasp is safe.
+define swifttailcc void @caller_to0_from0() "branch-protection-pauth-lr" "sign-return-address"="all" "frame-pointer"="all" nounwind uwtable(async) {
+; CHECK-LABEL: caller_to0_from0:
+; CHECK:       // %bb.0:
+
+; COMPAT-NEXT:   hint #39
+; COMPAT-NEXT:   .cfi_negate_ra_state_with_pc
+; COMPAT-NEXT: .Ltmp0:
+; COMPAT-NEXT:   hint #25
+
+; V83A-NEXT:     hint #39
+; V83A-NEXT:     .cfi_negate_ra_state_with_pc
+; V83A-NEXT:   .Ltmp0:
+; V83A-NEXT:     paciasp
+
+; V9A-NEXT:      .cfi_negate_ra_state_with_pc
+; V9A-NEXT:    .Ltmp0:
+; V9A-NEXT:      paciasppc
+
+; PAUTH-NEXT:    paciasp
+; PAUTH-NEXT:    .cfi_negate_ra_state
+
+; CHECK-NEXT:    stp x29, x30, [sp, #-16]! // 16-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    mov x29, sp
+; CHECK-NEXT:    .cfi_def_cfa w29, 16
+; CHECK-NEXT:    .cfi_offset w30, -8
+; CHECK-NEXT:    .cfi_offset w29, -16
+; CHECK-NEXT:    .cfi_def_cfa wsp, 16
+; CHECK-NEXT:    ldp x29, x30, [sp], #16 // 16-byte Folded Reload
+; CHECK-NEXT:    .cfi_def_cfa_offset 0
+; CHECK-NEXT:    .cfi_restore w30
+; CHECK-NEXT:    .cfi_restore w29
+
+; COMPAT-NEXT:   adrp x16, .Ltmp0
+; COMPAT-NEXT:   add x16, x16, :lo12:.Ltmp0
+; COMPAT-NEXT:   hint #39
+; COMPAT-NEXT:   .cfi_negate_ra_state_with_pc
+; COMPAT-NEXT:   hint #29
+
+; V83A-NEXT:     adrp x16, .Ltmp0
+; V83A-NEXT:     add x16, x16, :lo12:.Ltmp0
+; V83A-NEXT:     hint #39
+; V83A-NEXT:     .cfi_negate_ra_state_with_pc
+; V83A-NEXT:     autiasp
+
+; V9A-NEXT:      .cfi_negate_ra_state_with_pc
+; V9A-NEXT:      autiasppc .Ltmp0
+
+; PAUTH-NEXT:    autiasp
+; PAUTH-NEXT:    .cfi_negate_ra_state
+
+; CHECK-NEXT:    b callee_stack0
+  tail call swifttailcc void @callee_stack0()
+  ret void
+}
+
+; FPDiff > 0: caller received 8 bytes of stack args, callee receives 0.
+; SP has been bumped by the post-index LDP. We must authenticate with the
+; entry SP discriminator *before* popping the leftover argument space.
+; The correct sequence is: autiasp (SP == entry SP here), then add sp, #16.
+;
+; Key point: we must NOT bump SP before autiasp, because that leaves the
+; live argument space below SP and potentially outside the red-zone.
+define swifttailcc void @caller_to0_from8([8 x i64], i64) "branch-protection-pauth-lr" "sign-return-address"="all" "frame-pointer"="all" uwtable(async) {
+; CHECK-LABEL: caller_to0_from8:
+; CHECK:       // %bb.0:
+
+; COMPAT-NEXT:   hint #39
+; COMPAT-NEXT:   .cfi_negate_ra_state_with_pc
+; COMPAT-NEXT: .Ltmp1:
+; COMPAT-NEXT:   hint #25
+
+; V83A-NEXT:     hint #39
+; V83A-NEXT:     .cfi_negate_ra_state_with_pc
+; V83A-NEXT:   .Ltmp1:
+; V83A-NEXT:     paciasp
+
+; V9A-NEXT:      .cfi_negate_ra_state_with_pc
+; V9A-NEXT:    .Ltmp1:
+; V9A-NEXT:      paciasppc
+
+; PAUTH-NEXT:    paciasp
+; PAUTH-NEXT:    .cfi_negate_ra_state
+
+; CHECK-NEXT:    stp x29, x30, [sp, #-16]! // 16-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    mov x29, sp
+; CHECK-NEXT:    .cfi_def_cfa w29, 16
+; CHECK-NEXT:    .cfi_offset w30, -8
+; CHECK-NEXT:    .cfi_offset w29, -16
+; CHECK-NEXT:    .cfi_def_cfa wsp, 16
+; CHECK-NEXT:    ldp x29, x30, [sp], #16 // 16-byte Folded Reload
+; CHECK-NEXT:    .cfi_def_cfa_offset 0
+; CHECK-NEXT:    .cfi_def_cfa_offset -16
+; CHECK-NEXT:    .cfi_restore w30
+; CHECK-NEXT:    .cfi_restore w29
+
+; COMPAT-NEXT:   adrp x16, .Ltmp1
+; COMPAT-NEXT:   add x16, x16, :lo12:.Ltmp1
+; COMPAT-NEXT:   hint #39
+; COMPAT-NEXT:   .cfi_negate_ra_state_with_pc
+; COMPAT-NEXT:   hint #29
+
+; V83A-NEXT:     adrp x16, .Ltmp1
+; V83A-NEXT:     add x16, x16, :lo12:.Ltmp1
+; V83A-NEXT:     hint #39
+; V83A-NEXT:     .cfi_negate_ra_state_with_pc
+; V83A-NEXT:     autiasp
+
+; V9A-NEXT:      .cfi_negate_ra_state_with_pc
+; V9A-NEXT:      autiasppc .Ltmp1
+
+; PAUTH-NEXT:    autiasp
+; PAUTH-NEXT:    .cfi_negate_ra_state
+
+; CHECK-NEXT:    add sp, sp, #16
+; CHECK-NEXT:    b callee_stack0
+  tail call swifttailcc void @callee_stack0()
+  ret void
+}
+
+; FPDiff < 0: callee receives 8 bytes of stack args, caller received 0.
+; Entry SP is above current SP; reconstruct it in x16 and use autia1716.
+define swifttailcc void @caller_to8_from0() "branch-protection-pauth-lr" "sign-return-address"="all" "frame-pointer"="all" uwtable(async) {
+; CHECK-LABEL: caller_to8_from0:
+; CHECK:       // %bb.0:
+
+; COMPAT-NEXT:   hint #39
+; COMPAT-NEXT:   .cfi_negate_ra_state_with_pc
+; COMPAT-NEXT: .Ltmp2:
+; COMPAT-NEXT:   hint #25
+
+; V83A-NEXT:     hint #39
+; V83A-NEXT:     .cfi_negate_ra_state_with_pc
+; V83A-NEXT:   .Ltmp2:
+; V83A-NEXT:     paciasp
+
+; V9A-NEXT:      .cfi_negate_ra_state_with_pc
+; V9A-NEXT:    .Ltmp2:
+; V9A-NEXT:      paciasppc
+
+; PAUTH-NEXT:    paciasp
+; PAUTH-NEXT:    .cfi_negate_ra_state
+
+; CHECK-NEXT:    stp x29, x30, [sp, #-32]! // 16-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    mov x29, sp
+; CHECK-NEXT:    .cfi_def_cfa w29, 32
+; CHECK-NEXT:    .cfi_offset w30, -24
+; CHECK-NEXT:    .cfi_offset w29, -32
+; CHECK-NEXT:    mov w8, #42 // =0x2a
+; CHECK-NEXT:    str x8, [x29, #16]
+; CHECK-NEXT:    .cfi_def_cfa wsp, 32
+; CHECK-NEXT:    ldp x29, x30, [sp], #16 // 16-byte Folded Reload
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_restore w30
+; CHECK-NEXT:    .cfi_restore w29
+; CHECK-NEXT:    add x16, sp, #16
+; CHECK-NEXT:    mov x17, x30
+
+; COMPAT-NEXT:   adrp x15, .Ltmp2
+; COMPAT-NEXT:   add x15, x15, :lo12:.Ltmp2
+; COMPAT-NEXT:   hint #39
+; COMPAT-NEXT:   .cfi_negate_ra_state_with_pc
+; COMPAT-NEXT:   hint #12
+
+; V83A-NEXT:     adrp x15, .Ltmp2
+; V83A-NEXT:     add x15, x15, :lo12:.Ltmp2
+; V83A-NEXT:     hint #39
+; V83A-NEXT:     .cfi_negate_ra_state_with_pc
+; V83A-NEXT:     autia1716
+
+; V9A-NEXT:      adrp x15, .Ltmp2
+; V9A-NEXT:      add x15, x15, :lo12:.Ltmp2
+; V9A-NEXT:      .cfi_negate_ra_state_with_pc
+; V9A-NEXT:      autia171615
+
+; PAUTH-NEXT:    autia1716
+; PAUTH-NEXT:    .cfi_negate_ra_state
+
+; CHECK-NEXT:    mov x30, x17
+; CHECK-NEXT:    b callee_stack8
+  tail call swifttailcc void @callee_stack8([8 x i64] poison, i64 42)
+  ret void
+}


        


More information about the llvm-commits mailing list