[libunwind] 1aba434 - [libunwind][PAC] Defang ptrauth's PC in valid CFI range abort (#184041)

via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 27 07:33:13 PDT 2026


Author: Oliver Hunt
Date: 2026-03-27T14:33:08Z
New Revision: 1aba434dab1a628af4d2f370df3b468b79d31766

URL: https://github.com/llvm/llvm-project/commit/1aba434dab1a628af4d2f370df3b468b79d31766
DIFF: https://github.com/llvm/llvm-project/commit/1aba434dab1a628af4d2f370df3b468b79d31766.diff

LOG: [libunwind][PAC] Defang ptrauth's PC in valid CFI range abort (#184041)

It turns out making the CFI check a release mode abort causes many, if
not the majority, of JITs to fail during unwinding as they do not set up
CFI sections for their generated code. As a result any JITs that do
nominally support unwinding (and catching) through their JIT or assembly
frames trip this abort.

rdar://170862047

Added: 
    libunwind/test/set_non_null_pc_outside_of_function_cfi_bounds.pass.cpp

Modified: 
    libunwind/src/libunwind.cpp
    libunwind/test/libunwind_01.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index a795e68c57859..0b9d56ec805bc 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -131,23 +131,19 @@ _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
       {
         // It is only valid to set the IP within the current function. This is
         // important for ptrauth, otherwise the IP cannot be correctly signed.
-        // The current signature of `value` is via the schema:
-        //   __ptrauth(ptrauth_key_return_address, <<sp>>, 0)
-        // For this to be generally usable we manually re-sign it to the
-        // directly supported schema:
-        //   __ptrauth(ptrauth_key_return_address, 1, 0)
-        unw_word_t
-              __unwind_ptrauth_restricted_intptr(ptrauth_key_return_address, 1,
-                                                 0) authenticated_value;
-        unw_word_t opaque_value = (uint64_t)ptrauth_auth_and_resign(
-            (void *)value, ptrauth_key_return_address, sp,
-            ptrauth_key_return_address, &authenticated_value);
-        memmove(reinterpret_cast<void *>(&authenticated_value),
-                reinterpret_cast<void *>(&opaque_value),
-                sizeof(authenticated_value));
-        if (authenticated_value < info.start_ip ||
-            authenticated_value > info.end_ip)
-          _LIBUNWIND_ABORT("PC vs frame info mismatch");
+        //
+        // However many JITs do not configure CFI frames, so we cannot actually
+        // enforce this - at least not without an extremely expensive syscall.
+        //
+        // For the forseeable future this will need to be a debug only assertion
+        // so we just strip and assert to avoid the unnecessary auths in release
+        // builds.
+        unw_word_t stripped_value = (unw_word_t)ptrauth_strip(
+            (void *)value, ptrauth_key_return_address);
+        if (stripped_value < info.start_ip && stripped_value > info.end_ip)
+          _LIBUNWIND_LOG("Badly behaved use of unw_set_reg: moving IP(0x%zX) "
+                         "outside of CFI bounds function (0x%zX, 0x%zX)",
+                         stripped_value, info.start_ip, info.end_ip);
 
         // PC should have been signed with the sp, so we verify that
         // roundtripping does not fail. The `ptrauth_auth_and_resign` is

diff  --git a/libunwind/test/libunwind_01.pass.cpp b/libunwind/test/libunwind_01.pass.cpp
index 838df6b589720..b2dd12df70dde 100644
--- a/libunwind/test/libunwind_01.pass.cpp
+++ b/libunwind/test/libunwind_01.pass.cpp
@@ -79,7 +79,14 @@ void test_no_info() {
     abort();
 
   // Set the IP to an address clearly outside any function.
-  unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)0);
+  void *ip = 0;
+#if defined(__PTRAUTH__) || __has_feature(ptrauth_calls)
+  unw_word_t sp;
+  if (unw_get_reg(&cursor, UNW_REG_SP, &sp) != UNW_ESUCCESS)
+    abort();
+  ip = ptrauth_sign_unauthenticated(ip, ptrauth_key_return_address, (void *)sp);
+#endif
+  unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)ip);
 
   ret = unw_get_proc_info(&cursor, &info);
   if (ret != UNW_ENOINFO)

diff  --git a/libunwind/test/set_non_null_pc_outside_of_function_cfi_bounds.pass.cpp b/libunwind/test/set_non_null_pc_outside_of_function_cfi_bounds.pass.cpp
new file mode 100644
index 0000000000000..a7373b7ddecba
--- /dev/null
+++ b/libunwind/test/set_non_null_pc_outside_of_function_cfi_bounds.pass.cpp
@@ -0,0 +1,48 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: target={{(aarch64|s390x|x86_64|arm64e)-.+}}
+// UNSUPPORTED: target={{.*-windows.*}}
+
+// *SAN does not like our clearly nonsense personality and handler functions
+// which is the correct response for them, but alas we have to allow it for JITs
+// because they tend to use a shared handler rather than having the handler
+// within the function bounds.
+// UNSUPPORTED: asan
+// UNSUPPORTED: msan
+
+#include <libunwind.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unwind.h>
+
+extern "C" void exit(int);
+extern "C" void abort(void);
+
+void unrelated_function() {}
+
+int main(int, const char **) {
+  unw_context_t context;
+  unw_getcontext(&context);
+
+  unw_cursor_t cursor;
+  unw_init_local(&cursor, &context);
+
+  void *ip = (void *)&unrelated_function;
+#if defined(__PTRAUTH__) || __has_feature(ptrauth_calls)
+  unw_word_t sp;
+  if (unw_get_reg(&cursor, UNW_REG_SP, &sp) != UNW_ESUCCESS)
+    abort();
+  ip = ptrauth_auth_and_resign(ip, ptrauth_key_function_pointer, 0,
+                               ptrauth_key_return_address, (void *)sp);
+#endif
+  int ret = unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)ip);
+  if (ret != UNW_ESUCCESS)
+    abort();
+}


        


More information about the cfe-commits mailing list