diff --git a/src/backtrace/libunwind.rs b/src/backtrace/libunwind.rs index 0564f2ea..638cd8f8 100644 --- a/src/backtrace/libunwind.rs +++ b/src/backtrace/libunwind.rs @@ -79,6 +79,22 @@ 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: 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()) } } 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); }