[llvm] [AArch64][PAC] Combine signing with address materialization (PR #130809)
Anatoly Trosinenko via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 12 03:48:16 PDT 2025
atrosinenko wrote:
Here is an example of signing oracle observed in llvm-test-suite (for `MOVaddr -> MOVaddrPAC` case). When I compile `MultiSource/Benchmarks/Prolangs-C++/primes/primes.cpp` source with the following command:
```
/path/to/bin/clang++ -target aarch64-linux-pauthtest -march=v8.3-a --sysroot=... -stdlib=libc++ -O2 ../MultiSource/Benchmarks/Prolangs-C++/primes/primes.cpp -S -o- -mllvm -print-after=finalize-isel
```
I get the following (with CFI directives and most comments omitted for brevity):
<details>
<summary> `main` function (without this patch) </summary>
```
main:
// %bb.0: // %entry
pacibsp
sub sp, sp, #96
stp x29, x30, [sp, #48]
stp x22, x21, [sp, #64]
stp x20, x19, [sp, #80]
add x29, sp, #48
add x8, sp, #24
adrp x9, _ZTV7counter+16
add x9, x9, :lo12:_ZTV7counter+16
mov x10, x8
movk x10, #53906, lsl #48
add x11, sp, #8
mov w22, #34465
pacda x9, x10
adrp x10, _ZTV5sieve+16
add x10, x10, :lo12:_ZTV5sieve+16
movk x11, #53906, lsl #48
adrp x21, _ZTV6filter+16
add x21, x21, :lo12:_ZTV6filter+16
pacda x10, x11
mov w11, #2
adrp x19, .L.str
add x19, x19, :lo12:.L.str
movk w22, #1, lsl #16
str w11, [sp, #40]
stp x9, xzr, [sp, #24]
stp x10, x8, [sp, #8]
.LBB0_1: // %do.body
ldr x0, [sp, #16]
ldr x16, [x0]
mov x17, x0
movk x17, #53906, lsl #48
autda x16, x17
mov x17, x16
xpacd x17
cmp x16, x17
b.eq .Lauth_success_0
brk #0xc472
.Lauth_success_0:
ldr x8, [x16]
movk x16, #35396, lsl #48
blraa x8, x16
mov w20, w0
mov w0, #24
bl _Znwm // x21 can be tampered with by an attacker during the call
mov x8, x21 // x21 contains _ZTV6filter+16 under normal circumstances
mov x9, x0
movk x9, #53906, lsl #48
str w20, [x0, #16]
pacda x8, x9 // <-- signing oracle
ldr x9, [sp, #16]
str x0, [sp, #16]
mov w1, w20
stp x8, x9, [x0]
mov x0, x19
bl printf
cmp w20, w22
b.lt .LBB0_1
// %bb.2: // %do.end
mov w0, #10
bl putchar
mov w0, wzr
ldp x20, x19, [sp, #80]
ldp x22, x21, [sp, #64]
ldp x29, x30, [sp, #48]
add sp, sp, #96
retab
```
</details>
Under normal circumstances, the value of `_ZTV6filter+16` expression is stored to the callee-saved `x21` register at the beginning of the function and then signed with address diversity multiple times. The problem is that function call is performed between computing the address and signing it, so `x21` can be spilled to memory and tampered with by the attacker.
At the Machine IR level, after `finalize-isel` pass this looks like
```
%20:gpr64common = MOVaddr target-flags(aarch64-page) @_ZTV6filter + 16, target-flags(aarch64-pageoff, aarch64-nc) @_ZTV6filter + 16
%21:gpr64 = PACDA %20:gpr64common(tied-def 0), killed %19:gpr64common
```
With this patch applied, it gets rewritten like this (`MOVaddr` is expected to be dead-code-eliminated later, if not used):
```
%20:gpr64common = MOVaddr target-flags(aarch64-page) @_ZTV6filter + 16, target-flags(aarch64-pageoff, aarch64-nc) @_ZTV6filter + 16
%31:gpr64noip = COPY %19:gpr64common
MOVaddrPAC @_ZTV6filter + 16, 2, %31:gpr64noip, 0, implicit-def $x16, implicit-def $x17
%21:gpr64 = COPY $x16
```
This prevents signing oracle in `main` function:
<details>
<summary> `main` function (with this patch) </summary>
```
main:
// %bb.0: // %entry
pacibsp
sub sp, sp, #96
stp x29, x30, [sp, #48]
str x21, [sp, #64]
stp x20, x19, [sp, #80]
add x29, sp, #48
add x8, sp, #24
mov w9, #2
mov w21, #34465
mov x10, x8
movk x10, #53906, lsl #48
adrp x19, .L.str
add x19, x19, :lo12:.L.str
adrp x16, _ZTV7counter
add x16, x16, :lo12:_ZTV7counter
add x16, x16, #16
pacda x16, x10
str w9, [sp, #40]
add x9, sp, #8
movk x9, #53906, lsl #48
stp x16, xzr, [sp, #24]
movk w21, #1, lsl #16
adrp x16, _ZTV5sieve
add x16, x16, :lo12:_ZTV5sieve
add x16, x16, #16
pacda x16, x9
stp x16, x8, [sp, #8]
.LBB0_1: // %do.body
ldr x0, [sp, #16]
ldr x16, [x0]
mov x17, x0
movk x17, #53906, lsl #48
autda x16, x17
mov x17, x16
xpacd x17
cmp x16, x17
b.eq .Lauth_success_0
brk #0xc472
.Lauth_success_0:
ldr x8, [x16]
movk x16, #35396, lsl #48
blraa x8, x16
mov w20, w0
mov w0, #24
bl _Znwm
ldr x8, [sp, #16]
mov x9, x0
movk x9, #53906, lsl #48
mov w1, w20
adrp x16, _ZTV6filter
add x16, x16, :lo12:_ZTV6filter
add x16, x16, #16
pacda x16, x9 // <-- the address to sign cannot be spilled to memory
str w20, [x0, #16]
stp x16, x8, [x0]
str x0, [sp, #16]
mov x0, x19
bl printf
cmp w20, w21
b.lt .LBB0_1
// %bb.2: // %do.end
mov w0, #10
bl putchar
mov w0, wzr
ldp x20, x19, [sp, #80]
ldr x21, [sp, #64]
ldp x29, x30, [sp, #48]
add sp, sp, #96
retab
```
</summary>
https://github.com/llvm/llvm-project/pull/130809
More information about the llvm-commits
mailing list