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(); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
