[compiler-rt] [compiler-rt][sanitizer] Strip PAC from return addresses unconditionally on aarch64 (PR #218259)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 09:17:01 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-compiler-rt-sanitizer
Author: Avi Kivity (avikivity)
<details>
<summary>Changes</summary>
Fixes #<!-- -->218255.
`sanitizer_ptrauth.h` gates `STRIP_PAC_PC` on `__ARM_FEATURE_PAC_DEFAULT`, i.e. on whether the sanitizer runtime itself was compiled with `-mbranch-protection`. Its only real consumer is `StackTrace::UnwindFast`:
```c++
uhwptr pc1 = STRIP_PAC_PC((void *)frame[1]);
```
`frame[1]` is a return address spilled into a frame record by the code being unwound, so whether it carries a PAC is a property of *that* code, not of the runtime. The two are separate binaries and routinely disagree — a distro that builds userspace with `-mbranch-protection=standard` (Fedora does for aarch64) while compiler-rt is built from source with default flags gets a runtime with the strip compiled out, unwinding an application whose frame records are all signed. The fast unwinder then records garbage for every allocation: nonsense frames in reports, and LSan suppressions that silently never match.
The header's own comment already describes the intended behaviour — *"Let's stripping the PAC unconditionally because xpaclri is in the NOP space so will do nothing when it is not enabled or not available"* — so this just makes the code agree with it, and rewrites the comment to say why the condition cannot be tested for here.
`XPACLRI` is `HINT #<!-- -->7`: a NOP on cores without FEAT_PAuth, and the identity on an unsigned canonical address, so it is safe to run unconditionally.
### Effect
Compiling the header's only real use, cross-compiled with no `-mbranch-protection`:
```c++
typedef unsigned long uptr;
#include "sanitizer_common/sanitizer_ptrauth.h"
extern "C" uptr next_pc(uptr pc) { return STRIP_PAC_PC((void *)pc) + 4; }
```
Before:
```
0000000000000000 <next_pc>:
0: 91001000 add x0, x0, #<!-- -->0x4
4: d65f03c0 ret
```
After:
```
0000000000000000 <next_pc>:
0: f81f0ffe str x30, [sp, #-0x10]!
4: aa0003fe mov x30, x0
8: d50320ff xpaclri
c: aa1e03e8 mov x8, x30
10: aa1f03fe mov x30, xzr
14: 91001100 add x0, x8, #<!-- -->0x4
18: f84107fe ldr x30, [sp], #<!-- -->0x10
1c: d65f03c0 ret
```
Apple targets are unaffected: `__has_feature(ptrauth_intrinsics)` still takes precedence, and the `!defined(__APPLE__)` guard is kept.
No test — reproducing the failure needs an aarch64 host with FEAT_PAuth running a PAC-built process under ASan, which the sanitizer_common test suite has no way to require. The change is visible in codegen as shown above.
---
Full diff: https://github.com/llvm/llvm-project/pull/218259.diff
1 Files Affected:
- (modified) compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h (+8-5)
``````````diff
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h b/compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h
index 265a9925a15a0..ea16a032047ab 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h
@@ -11,11 +11,14 @@
#if __has_feature(ptrauth_intrinsics)
# include <ptrauth.h>
-#elif defined(__ARM_FEATURE_PAC_DEFAULT) && !defined(__APPLE__)
-// On the stack the link register is protected with Pointer
-// Authentication Code when compiled with -mbranch-protection.
-// Let's stripping the PAC unconditionally because xpaclri is in
-// the NOP space so will do nothing when it is not enabled or not available.
+#elif defined(__aarch64__) && !defined(__APPLE__)
+// On the stack the link register is protected with a Pointer Authentication
+// Code when the code that spilled it was compiled with -mbranch-protection.
+// That is a property of the code being unwound, not of this runtime, so it
+// cannot be tested for here (__ARM_FEATURE_PAC_DEFAULT would describe the
+// wrong binary). Strip unconditionally instead: xpaclri is in the NOP space,
+// so it does nothing where pointer authentication is not enabled or not
+// available, and it is the identity on an unsigned canonical address.
# define ptrauth_strip(__value, __key) \
({ \
__typeof(__value) ret; \
``````````
</details>
https://github.com/llvm/llvm-project/pull/218259
More information about the llvm-commits
mailing list