[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