Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/backtrace/libunwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not check for the respective target feature being enabled? Not every target with pointer authentication enabled will use that target env, right?

// 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()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is ip not signed? It is directly the result of _Unwind_GetIP. It should be possible to pass that to _Unwind_FindEnclosingFunction. How else would you even be able to use _Unwind_FindEnclosingFunction otherwise?

} else {
unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
}
Expand Down
6 changes: 6 additions & 0 deletions tests/skip_inner_frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
12 changes: 11 additions & 1 deletion tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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);
}
Expand Down
Loading