[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
This commit is contained in:
parent
3bff1a81c1
commit
1aba434dab
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user