[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