From e6cc54b4beed83a89cf5f84671a5e5b26a87dbbd Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 20 Apr 2026 13:12:28 +0000 Subject: [PATCH 1/3] Add support for `aarch64-unknown-linux-pauthtest` This PR adds support for `aarch64-unknown-linux-pauthtest`, a target that enables Pointer Authentication Code (PAC) support in Rust on AArch64 ELF based Linux systems using a pauthtest ABI (provided by LLVM) and pauthtest-enabled sysroot with custom musl, serving as a reference libc implementation. --- src/backtrace/libunwind.rs | 12 ++++++++++++ tests/skip_inner_frames.rs | 6 ++++++ tests/smoke.rs | 12 +++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/backtrace/libunwind.rs b/src/backtrace/libunwind.rs index 0564f2ea..a8a0d1a7 100644 --- a/src/backtrace/libunwind.rs +++ b/src/backtrace/libunwind.rs @@ -79,6 +79,18 @@ impl Frame { // clause, and if this is fixed that test in theory can be run on macOS! if cfg!(target_vendor = "apple") { self.ip() + } else if cfg!(target_env = "pauthtest") { + // NOTE: As ip here is not signed (raw, non-PAC-enabled pointer) we + // must not use uw::_Unwind_FindEnclosingFunction. This is because, + // for pauthtest toolchain, libunwind will try to authenticate and + // resign it. Signing here (apart from risking creating a signing + // oracle) is not possible. According to the schema the value must + // be signed using SP as the discriminator - which is the problem. + // SP obtained here would not match the SP at the auth-resign time, + // as uw::_Unwind_FindEnclosingFunction creates a new context so + // the SP used for signing here would belong to a different frame + // that the one used for auth-resign. Hence return a raw value. + self.ip() } else { unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } } diff --git a/tests/skip_inner_frames.rs b/tests/skip_inner_frames.rs index e62a1603..f39cdf71 100644 --- a/tests/skip_inner_frames.rs +++ b/tests/skip_inner_frames.rs @@ -10,6 +10,12 @@ const ENABLED: bool = cfg!(all( target_os = "linux", // On ARM finding the enclosing function is simply returning the ip itself. not(target_arch = "arm"), + // On `aarch64-unknown-linux-pauthtest` `_Unwind_FindEnclosingFunction` + // cannot be used safely, because instruction pointers are not in a form + // suitable for authentication/resigning, so `symbol_address()` returns the + // raw IP instead. In this case the returned address may be inside the + // function body rather than at its entry. + not(target_env = "pauthtest"), )); #[test] diff --git a/tests/smoke.rs b/tests/smoke.rs index fd5684f9..fa789f1a 100644 --- a/tests/smoke.rs +++ b/tests/smoke.rs @@ -25,6 +25,13 @@ fn get_actual_fn_pointer(fp: *mut c_void) -> *mut c_void { } #[test] +// This test relies on recovering precise symbol addresses from instruction +// pointers. On `aarch64-unknown-linux-pauthtest`, we cannot safely use +// `_Unwind_FindEnclosingFunction`, and instead fall back to using raw +// instruction pointers. As a result, symbol resolution cannot reliably +// recover a canonical function entry address, and `sym.addr()` will often be +// `None`. Therefore this test is disabled. +#[cfg_attr(target_env = "pauthtest", ignore)] // FIXME: shouldn't ignore this test on i686-msvc, unsure why it's failing #[cfg_attr(all(target_arch = "x86", target_env = "msvc"), ignore)] #[inline(never)] @@ -310,7 +317,10 @@ fn sp_smoke_test() { let r = refs.pop().unwrap(); eprintln!("ref = {:p}", r); if sp as usize != 0 { - assert!(r > sp); + // Stack grows down, however stack slots can be reused and + // frame pointers ommited, `r == sp` should be a valid edge + // case. + assert!(r >= sp); if let Some(child_ref) = child_ref { assert!(sp >= child_ref); } From 67a02e04bb966f26fb4e77102c77e417f3b6f3e8 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 24 Apr 2026 12:26:07 +0000 Subject: [PATCH 2/3] Reword the note --- src/backtrace/libunwind.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/backtrace/libunwind.rs b/src/backtrace/libunwind.rs index a8a0d1a7..638cd8f8 100644 --- a/src/backtrace/libunwind.rs +++ b/src/backtrace/libunwind.rs @@ -80,16 +80,20 @@ impl Frame { if cfg!(target_vendor = "apple") { self.ip() } else if cfg!(target_env = "pauthtest") { - // NOTE: As ip here is not signed (raw, non-PAC-enabled pointer) we - // must not use uw::_Unwind_FindEnclosingFunction. This is because, - // for pauthtest toolchain, libunwind will try to authenticate and - // resign it. Signing here (apart from risking creating a signing - // oracle) is not possible. According to the schema the value must - // be signed using SP as the discriminator - which is the problem. - // SP obtained here would not match the SP at the auth-resign time, - // as uw::_Unwind_FindEnclosingFunction creates a new context so - // the SP used for signing here would belong to a different frame - // that the one used for auth-resign. Hence return a raw value. + // NOTE: ip here is an unsigned (raw) pointer, so we must not use + // uw::_Unwind_FindEnclosingFunction. + // + // Otherwise, in the pointer-authentication-enabled reference + // toolchain, libunwind would attempt to authenticate and re-sign + // values. Performing signing here is not safe: it could create a + // signing oracle, and more importantly it is incorrect under the + // expected signing schema. + // The schema requires the stack pointer (SP) as the discriminator. + // However, the SP available at this point would not match the SP + // at authentication/re-sign time, since + // _Unwind_FindEnclosingFunction constructs a new unwind context. + // The SP used here would therefore correspond to a different frame. + // As a result, we must return the raw value. self.ip() } else { unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } From cfa50f4abc37639456d0a9e1f0f1dfeb1dcccfb9 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 11 May 2026 13:00:23 +0000 Subject: [PATCH 3/3] Use target_abi = "pauthtest" (instead of target_env) --- src/backtrace/libunwind.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backtrace/libunwind.rs b/src/backtrace/libunwind.rs index 638cd8f8..b27a9f9f 100644 --- a/src/backtrace/libunwind.rs +++ b/src/backtrace/libunwind.rs @@ -79,7 +79,7 @@ impl Frame { // clause, and if this is fixed that test in theory can be run on macOS! if cfg!(target_vendor = "apple") { self.ip() - } else if cfg!(target_env = "pauthtest") { + } else if cfg!(target_abi = "pauthtest") { // NOTE: ip here is an unsigned (raw) pointer, so we must not use // uw::_Unwind_FindEnclosingFunction. //