[llvm] [AArch64][PAC] Protect the entire function if pac-ret+leaf is passed (PR #140895)
Anatoly Trosinenko via llvm-commits
llvm-commits at lists.llvm.org
Tue May 27 08:53:33 PDT 2025
atrosinenko wrote:
> Do you have some examples where the +leaf really improves the security?
> If a given function spills LR then it got protected anyway by pac-ret.
@DanielKristofKiss I'm not sure what was the original reason to introduce "sign every function" mode, but I have managed to reproduce LR substitution with signal handling. Consider the following program:
```c
#include <signal.h>
#include <stdio.h>
#include <sys/ucontext.h>
#include <unistd.h>
long * volatile volatile_ptr_to_null;
void unreachable(void) {
puts("In unreachable().");
_exit(1);
}
void handler(int num, siginfo_t *si, void *ctx_) {
ucontext_t *ctx = ctx_;
ctx->uc_mcontext.pc += 4; // skip faulting instruction
#if 0
ctx->uc_mcontext.regs[30] = (unsigned long long)&unreachable;
#endif
puts("In handler().");
}
__attribute__((noinline)) void callee(void) {
*volatile_ptr_to_null = 1;
}
int main() {
struct sigaction sa = {
.sa_sigaction = &handler,
.sa_flags = SA_SIGINFO,
};
sigaction(SIGSEGV, &sa, NULL);
callee();
puts("Returning from main...");
return 0;
}
```
It involves an access to unreachable memory, so it could be undefined behavior, but I hope using a `volatile` pointer makes it opaque to the optimizer. If I compile the above source with
```
clang -target aarch64-linux-gnu -fuse-ld=lld example.c -o example -O2 -mbranch-protection=pac-ret
```
it prints
```
In handler().
Returning from main...
```
The same output is produced with `pac-ret+leaf`.
With `-mbranch-protection=pac-ret` the `callee` function is emitted as follows:
```
<callee>:
adrp x8, 0xPAGE_ADDR
mov w9, #0x1
ldr x8, [x8, #0xOFFSET]
str x9, [x8]
ret
```
and with `-mbranch-protection=pac-ret+leaf`:
```
<callee>:
paciasp
adrp x8, 0xPAGE_ADDR
mov w9, #0x1
ldr x8, [x8, #0xOFFSET]
str x9, [x8]
autiasp
ret
```
If I uncomment the assignment to `ctx->uc_mcontext.regs[30]`, with `pac-ret` handrening mode the following is printed:
```
In handler().
In unreachable().
```
and with `pac-ret+leaf` it prints `In handler().` line infinitely.
This example doesn't look too synthetic. Even if a sigaction-style signal handler does not actually access register values, the registers are still spilled to a memory area accessible from user space and, thus, can technically be overwritten by another thread.
https://github.com/llvm/llvm-project/pull/140895
More information about the llvm-commits
mailing list