[libunwind] [WIP][PAC][libunwind] Handle LR and IP signing around sigreturn frame (PR #184661)

Anatoly Trosinenko via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 5 03:57:06 PST 2026


================
@@ -2944,8 +2950,14 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_arm64 &) {
                                          static_cast<pint_t>(i * 8));
     _registers.setRegister(UNW_AARCH64_X0 + i, value);
   }
-  _registers.setSP(_addressSpace.get64(sigctx + kOffsetSp));
-  _registers.setIP(_addressSpace.get64(sigctx + kOffsetPc));
+  uint64_t sp = _addressSpace.get64(sigctx + kOffsetSp);
+  uint64_t ip = _addressSpace.get64(sigctx + kOffsetPc);
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+  ip = (uint64_t)ptrauth_sign_unauthenticated((void *)ip,
+                                              ptrauth_key_return_address, sp);
----------------
atrosinenko wrote:

As far as I can see, this value is read from an `mcontext_t` structure written to the stack by the Linux kernel itself. This is the value that was in IP at the time regular execution flow was interrupted by the kernel to invoke SIGSEGV handler.

Here is what I see in the debugger running inside QEMU just before SIGSEGV (full system emulation, not qemu-user; simulated CPU is `neoverse-v1` and the guest kernel is rebuilt with narrower virtual addresses / wider PAC field):
```
(gdb) x/7i Signaller
   0x5555560ff0 <_Z9Signallerv>:        pacibsp
   0x5555560ff4 <_Z9Signallerv+4>:      str     x30, [sp, #-16]!
   0x5555560ff8 <_Z9Signallerv+8>:      adrp    x8, 0x5555581000
   0x5555560ffc <_Z9Signallerv+12>:     ldr     x8, [x8, #2408]
=> 0x5555561000 <_Z9Signallerv+16>:     str     xzr, [x8]
   0x5555561004 <_Z9Signallerv+20>:     ldr     x30, [sp], #16
   0x5555561008 <_Z9Signallerv+24>:     retab
(gdb) p/x $sp
$1 = 0x7ffffff290
(gdb) p/x $lr
$2 = 0x28c6d555561040
```
After stepping to the next instruction, signal handler is invoked:
```
(gdb) x/4i $pc
=> 0x5555560f8c <_Z7Handleri>:  pacibsp
   0x5555560f90 <_Z7Handleri+4>:        sub     sp, sp, #0x30
   0x5555560f94 <_Z7Handleri+8>:        stp     x29, x30, [sp, #32]
   0x5555560f98 <_Z7Handleri+12>:       add     x29, sp, #0x20
(gdb) p/x $sp
$4 = 0x7fffffe030
(gdb) p/x $lr
$5 = 0x7ff7fbadc4
(gdb) x/2i $lr
   0x7ff7fbadc4 <__restore_rt>: mov     x8, #0x8b                       // #139
   0x7ff7fbadc8 <__restore_rt+4>:       svc     #0x0
```
Note that SP was decremented by 0x1260 and LR was arranged so that when our signal handler returns, execution is transferred to `__restore_rt` (as far as I understand, in this particular case `__restore_rt` is implemented by musl libc and its address is passed to the kernel in `sa_restorer` field of `struct sigaction` by the libc itself). LR is not signed at this point but this is correct: it is signed by the `Handler`'s prologue as usual. Invocation of `Handler` is unwound as usual thanks to CFI information emitted for `Handler` and then it is `__restore_rt` frame that `setInfoForSigReturn` and `stepThroughSigReturn` deal with.

Here is what is stored on the stack (by the kernel, I assume) at several offsets mentioned in `stepThroughSigReturn` function's implementation for AArch64:
```
# SP:

(gdb) p/x *(uintptr_t *)($sp + 304 + 256)
$7 = 0x7ffffff290

# PC:

(gdb) p/x *(uintptr_t *)($sp + 304 + 264)
$8 = 0x5555561000
(gdb) x/3i 0x5555561000
   0x5555561000 <_Z9Signallerv+16>:     str     xzr, [x8]
   0x5555561004 <_Z9Signallerv+20>:     ldr     x30, [sp], #16
   0x5555561008 <_Z9Signallerv+24>:     retab

# LR:

(gdb) p/x *(uintptr_t *)($sp + 304 + 8 + 30 * 8)
$9 = 0x28c6d555561040
(gdb) x/4i (0x28c6d555561040 & 0x7fffffffff)
   0x5555561040 <_Z6Calleri+52>:        b       0x5555561044 <_Z6Calleri+56>
   0x5555561044 <_Z6Calleri+56>:        ldp     x29, x30, [sp, #16]
   0x5555561048 <_Z6Calleri+60>:        add     sp, sp, #0x20
   0x555556104c <_Z6Calleri+64>:        retab
```

The registers' values are stored exactly as they were immediately before the execution of faulted `str` instruction.

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


More information about the cfe-commits mailing list