[llvm] [AArch64][PAC] Combine signing with address materialization (PR #130809)

Anatoly Trosinenko via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 3 11:41:53 PDT 2025


atrosinenko wrote:

Ping.

I have updated this PR to account for possibility of immediate modifier substitution:
```
  movk    x10, #1234, #48
  bl      callee
  adrp    x17, :got:sym
  add     x17, x17, :got_lo12:sym
  pacda   x17, x10
```
In the above example, the discriminator is computed as `blend(addr, 1234)` - while it is impossible to prevent substitution of `addr` in general, it is definitely possible to at least make sure that 1234 is always blended into whatever is stored in `x10` after `callee` returns (in the below code `x10` is used for clarity - with this PR applied `x16` would be used instead):
```
  bl      callee
  adrp    x17, :got:sym
  add     x17, x17, :got_lo12:sym
  movk    x10, #1234, #48
  pacda   x17, x10
```

In their current state, both this PR and #133788 protect both pointer-to-be-signed as well as the immediate modifier from tampering, but #133788 still has to be updated to handle GlobalISel in addition to DAGISel.

Hopefully, having a unified post-processing at the `MachineInstr` level instead of two separate implementations in DAGISel and GlobalISel could improve maintainability - though, it may be my bias towards `MachineFunction` passes vs. instruction selectors. Furthermore, it feels like with custom inserters it should be possible to extend #132857 to completely get rid of DAGIsel- and GlobalISel-specific selectors for AUT and PAC and use the selectors tablegen-erated from declarative patterns.

Another feature of custom inserters seems to be better handling of cross-basic-block analysis. If I understand it right, at finalize-isel stage virtual registers are in SSA form, and it should be safe to ignore basic blocks while following the chains of copy and increment/decrement instructions, as long as only virtual registers are traversed and no PHI instructions occur.

```llvm
@var = global i32 0
@discvar = global i64 0

define i64 @foo(i1 %cond) {
entry:
  %addrdisc = load i64, ptr @discvar
  %disc = call i64 @llvm.ptrauth.blend(i64 %addrdisc, i64 42)
  %var.ptr.i = ptrtoint ptr @var to i64
  br i1 %cond, label %next, label %exit

next:
  call void asm sideeffect "nop", "r"(i64 %disc)
  br label %exit

exit:
  %signed = call i64 @llvm.ptrauth.sign(i64 %var.ptr.i, i32 2, i64 %disc)
  ret i64 %signed
}

!llvm.module.flags = !{!0}
!0 = !{i32 8, !"ptrauth-elf-got", i32 1}
```
here the DAGISel-based patch is unable to provide a `PtrAuthGlobalAddress` node because discriminator computation is in another basic block (note the `bb.2.exit` basic block in the dump after `finalize-isel` pass):

<details>
<summary>DAGISel-based patch</summary>

```
# ./bin/clang -target aarch64-linux-pauthtest -march=armv8.3-a /tmp/repro.ll -S -o- -O2 -mllvm -print-after=codegenprepare,finalize-isel
warning: overriding the module target triple with aarch64-unknown-linux-pauthtest [-Woverride-module]
        .file   "repro.ll"
*** IR Dump After CodeGen Prepare (codegenprepare) ***
define i64 @foo(i1 %cond) local_unnamed_addr {
entry:
  %addrdisc = load i64, ptr @discvar, align 8
  %disc = tail call i64 @llvm.ptrauth.blend(i64 %addrdisc, i64 42)
  br i1 %cond, label %next, label %exit

next:                                             ; preds = %entry
  tail call void asm sideeffect "nop", "r"(i64 %disc) #1
  br label %exit

exit:                                             ; preds = %next, %entry
  %signed = tail call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @var to i64), i32 2, i64 %disc)
  ret i64 %signed
}
# *** IR Dump After Finalize ISel and expand pseudo-instructions (finalize-isel) ***:
# Machine code for function foo: IsSSA, TracksLiveness
Function Live Ins: $w0 in %1

bb.0.entry:
  successors: %bb.1(0x40000000), %bb.2(0x40000000); %bb.1(50.00%), %bb.2(50.00%)
  liveins: $w0
  %1:gpr32 = COPY $w0
  %2:gpr64common = LOADgotAUTH target-flags(aarch64-got) @discvar, implicit-def dead $x16, implicit-def dead $x17, implicit-def dead $nzcv
  %3:gpr64 = LDRXui killed %2:gpr64common, 0 :: (dereferenceable load (s64) from @discvar)
  %4:gpr64 = MOVKXi %3:gpr64(tied-def 0), 42, 48
  %0:gpr64sp = COPY %4:gpr64
  TBZW %1:gpr32, 0, %bb.2
  B %bb.1

bb.1.next:
; predecessors: %bb.0
  successors: %bb.2(0x80000000); %bb.2(100.00%)

  %5:gpr64common = COPY %0:gpr64sp
  INLINEASM &nop [sideeffect] [attdialect], $0:[reguse:GPR64common], %5:gpr64common

bb.2.exit:
; predecessors: %bb.0, %bb.1

  %6:gpr64common = LOADgotAUTH target-flags(aarch64-got) @var, implicit-def dead $x16, implicit-def dead $x17, implicit-def dead $nzcv
  %7:gpr64 = PACDA %6:gpr64common(tied-def 0), %0:gpr64sp
  $x0 = COPY %7:gpr64
  RET_ReallyLR implicit $x0

# End machine code for function foo.

        .text
        .globl  foo                             // -- Begin function foo
        .p2align        2
        .type   foo, at function
foo:                                    // @foo
        .cfi_startproc
// %bb.0:                               // %entry
        adrp    x17, :got_auth:discvar
        add     x17, x17, :got_auth_lo12:discvar
        ldr     x16, [x17]
        autda   x16, x17
        mov     x17, x16
        xpacd   x17
        cmp     x16, x17
        b.eq    .Lauth_success_0
        brk     #0xc472
.Lauth_success_0:
        mov     x8, x16
        ldr     x8, [x8]
        movk    x8, #42, lsl #48
        tbz     w0, #0, .LBB0_2
// %bb.1:                               // %next
        //APP
        nop
        //NO_APP
.LBB0_2:                                // %exit
        adrp    x17, :got_auth:var
        add     x17, x17, :got_auth_lo12:var
        ldr     x16, [x17]
        autda   x16, x17
        mov     x17, x16
        xpacd   x17
        cmp     x16, x17
        b.eq    .Lauth_success_1
        brk     #0xc472
.Lauth_success_1:
        mov     x0, x16
        pacda   x0, x8
        ret
.Lfunc_end0:
        .size   foo, .Lfunc_end0-foo
        .cfi_endproc
                                        // -- End function
        .type   var, at object                     // @var
        .bss
        .globl  var
        .p2align        2, 0x0
var:
        .word   0                               // 0x0
        .size   var, 4

        .type   discvar, at object                 // @discvar
        .globl  discvar
        .p2align        3, 0x0
discvar:
        .xword  0                               // 0x0
        .size   discvar, 8

        .section        ".note.GNU-stack","", at progbits
        .addrsig
        .addrsig_sym var
1 warning generated.
```

</details>

<details>
<summary>Custom inserter-based patch</summary>

```
# ./bin/clang -target aarch64-linux-pauthtest -march=armv8.3-a /tmp/repro.ll -S -o- -O2 -mllvm -print-after=codegenprepare,finalize-isel
warning: overriding the module target triple with aarch64-unknown-linux-pauthtest [-Woverride-module]
        .file   "repro.ll"
*** IR Dump After CodeGen Prepare (codegenprepare) ***
define i64 @foo(i1 %cond) local_unnamed_addr {
entry:
  %addrdisc = load i64, ptr @discvar, align 8
  %disc = tail call i64 @llvm.ptrauth.blend(i64 %addrdisc, i64 42)
  br i1 %cond, label %next, label %exit

next:                                             ; preds = %entry
  tail call void asm sideeffect "nop", "r"(i64 %disc) #1
  br label %exit

exit:                                             ; preds = %next, %entry
  %signed = tail call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @var to i64), i32 2, i64 %disc)
  ret i64 %signed
}
# *** IR Dump After Finalize ISel and expand pseudo-instructions (finalize-isel) ***:
# Machine code for function foo: IsSSA, TracksLiveness
Function Live Ins: $w0 in %1

bb.0.entry:
  successors: %bb.1(0x40000000), %bb.2(0x40000000); %bb.1(50.00%), %bb.2(50.00%)
  liveins: $w0
  %1:gpr32 = COPY $w0
  %2:gpr64common = LOADgotAUTH target-flags(aarch64-got) @discvar, implicit-def dead $x16, implicit-def dead $x17, implicit-def dead $nzcv
  %3:gpr64 = LDRXui killed %2:gpr64common, 0 :: (dereferenceable load (s64) from @discvar)
  %4:gpr64 = MOVKXi %3:gpr64(tied-def 0), 42, 48
  %0:gpr64sp = COPY %4:gpr64
  TBZW %1:gpr32, 0, %bb.2
  B %bb.1

bb.1.next:
; predecessors: %bb.0
  successors: %bb.2(0x80000000); %bb.2(100.00%)

  %5:gpr64common = COPY %0:gpr64sp
  INLINEASM &nop [sideeffect] [attdialect], $0:[reguse:GPR64common], %5:gpr64common

bb.2.exit:
; predecessors: %bb.0, %bb.1

  %6:gpr64common = LOADgotAUTH target-flags(aarch64-got) @var, implicit-def dead $x16, implicit-def dead $x17, implicit-def dead $nzcv
  %8:gpr64noip = COPY %3:gpr64
  LOADgotPAC target-flags(aarch64-got) @var, 2, %8:gpr64noip, 42, implicit-def $x16, implicit-def $x17, implicit-def $nzcv
  %7:gpr64 = COPY $x16
  $x0 = COPY %7:gpr64
  RET_ReallyLR implicit $x0

# End machine code for function foo.

        .text
        .globl  foo                             // -- Begin function foo
        .p2align        2
        .type   foo, at function
foo:                                    // @foo
        .cfi_startproc
// %bb.0:                               // %entry
        adrp    x17, :got_auth:discvar
        add     x17, x17, :got_auth_lo12:discvar
        ldr     x16, [x17]
        autda   x16, x17
        mov     x17, x16
        xpacd   x17
        cmp     x16, x17
        b.eq    .Lauth_success_0
        brk     #0xc472
.Lauth_success_0:
        mov     x8, x16
        ldr     x8, [x8]
        tbz     w0, #0, .LBB0_2
// %bb.1:                               // %next
        mov     x9, x8
        movk    x9, #42, lsl #48
        //APP
        nop
        //NO_APP
.LBB0_2:                                // %exit
        adrp    x17, :got_auth:var
        add     x17, x17, :got_auth_lo12:var
        ldr     x16, [x17]
        autda   x16, x17
        mov     x17, x16
        xpacd   x17
        cmp     x16, x17
        b.eq    .Lauth_success_1
        brk     #0xc472
.Lauth_success_1:
        mov     x17, x8
        movk    x17, #42, lsl #48
        pacda   x16, x17
        mov     x0, x16
        ret
.Lfunc_end0:
        .size   foo, .Lfunc_end0-foo
        .cfi_endproc
                                        // -- End function
        .type   var, at object                     // @var
        .bss
        .globl  var
        .p2align        2, 0x0
var:
        .word   0                               // 0x0
        .size   var, 4

        .type   discvar, at object                 // @discvar
        .globl  discvar
        .p2align        3, 0x0
discvar:
        .xword  0                               // 0x0
        .size   discvar, 8

        .section        ".note.GNU-stack","", at progbits
        .addrsig
        .addrsig_sym var
1 warning generated.
```

</details>

https://github.com/llvm/llvm-project/pull/130809


More information about the llvm-commits mailing list