Skip to content

Add support for aarch64-unknown-linux-pauthtest#755

Draft
jchlanda wants to merge 1 commit intorust-lang:masterfrom
jchlanda:jakub/pauthtest
Draft

Add support for aarch64-unknown-linux-pauthtest#755
jchlanda wants to merge 1 commit intorust-lang:masterfrom
jchlanda:jakub/pauthtest

Conversation

@jchlanda
Copy link
Copy Markdown

@jchlanda jchlanda commented Apr 20, 2026

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.

Please consult a rust-lang PR for the details on the target: TODO JKB Add link

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.
// 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?

Copy link
Copy Markdown
Author

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.

A raw ip is expected. _Unwind_GetIP authenticates the pointer and returns a stripped version (via ptrauth_auth_data here).

It should be possible to pass that to _Unwind_FindEnclosingFunction. How
else would you even be able to use _Unwind_FindEnclosingFunction otherwise?

This is the crux of the issue. _Unwind_FindEnclosingFunction is fundamentally incompatible with the pointer authentication model. The problem is that _Unwind_FindEnclosingFunction does not follow PAC semantics, it blindly creates a fresh unwind cursor and then resets the instruction pointer here: __unw_set_reg(cursor, UNW_REG_IP, pc);. As if to say: "treat this pc as if it belonged to this new cursor's frame".

__unw_set_reg does apply PAC logic internally (authenticates) under the assumption that the ip behaves like a return address for the current frame.

This cannot be fixed on the Rust side. _Unwind_FindEnclosingFunction constructs a new cursor (and therefore a new sp), so there is no way to correctly sign pointer: any signing we would perform would use the wrong context and would still fail authentication inside libunwind.

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.

That sounds like a bug in libunwind to me. What purpose does _Unwind_FindEnclosingFunction have at all if you can't pass the result of _Unwind_GetIP to it?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'm not sure, the documentation seems to be pretty scarce, and one I found is rather quite lax about possible outcomes of the call, explicitly allowing failure.

I personally don't read it as a bug. To me it is more that _Unwind_FindEnclosingFunction was designed before pointer authentication existed, and its API implicitly assumes that instruction pointers are context-free values. But in PAC-aware code PC is only meaningful in the context of the (original) SP.

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 does _Unwind_FindEnclosingFunction need an authenticated pointer? It doesn't dereference the pointer, only looks it up in a side table with the result not having any security relevance. The only useful thing you can do with it afaik is looking up debuginfo for the function. Due to inlining and outlining you can't guarantee that it points to any particular function.

// 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?

Copy link
Copy Markdown
Author

@jchlanda jchlanda Apr 23, 2026

Choose a reason for hiding this comment

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

You are right.
We're in the middle of preparing an upstream patch that introduces a pointer authentication aware target. It just so happens that in there I ended up using (incorrectly) the target environment as the gating mechanism.

We have a follow up task to resolve the target to a set of features, just like it's done in clang where it solves platform/environment specifics on the driver level and later on everything is based on language flags that are there regardless of the platform. And finally are resolved to a concrete signing schema.

This will become a bit more clear once the PR lands and I submit a ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants