[libunwind] `_Unwind_FindEnclosingFunction` fails on AArch64 with pointer authentication enabled
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
On AArch64 with pointer authentication enabled `_Unwind_FindEnclosingFunction` cannot succeed for an instruction pointer obtained from `_Unwind_GetIP`. This surfaced while adding `aarch64-unknown-linux-pauthtest` support to Rust's backtrace-rs: https://github.com/rust-lang/backtrace-rs/pull/755 ([the same Rust workaround](https://github.com/rust-lang/backtrace-rs/blob/master/src/backtrace/libunwind.rs#L81) - not calling into `_Unwind_FindEnclosingFunction` - is used for `arm64e`, so it's possible that it is also affected by this issue).
`_Unwind_GetIP` authenticates the stored PC and [returns an instruction pointer value associated with the original frame context](https://github.com/llvm/llvm-project/blob/main/libunwind/src/Registers.hpp#L1894), signed using the frame's SP as discriminator. That value is what a caller, like backtrace-rs, keeps as the frame's IP.
The failure occurs when `_Unwind_FindEnclosingFunction` is called and expected to [return the start address of the function enclosing a given PC](https://github.com/llvm/llvm-project/blob/main/libunwind/src/UnwindLevel1-gcc-ext.c#L85). However, the cursor is **not** the original frame that produced PC. It's a brand new unwind cursor initialized from the current thread context (wherever `_Unwind_FindEnclosingFunction` itself happens to be called from).
Because the instruction pointer returned by `_Unwind_GetIP` is discriminated with SP, its subsequent authentication depends on the original frame context, specifically the SP used for discrimination when signing. When `_Unwind_FindEnclosingFunction` imports this IP into its newly created cursor, it goes through `__unw_set_reg`, which [calls](https://github.com/llvm/llvm-project/blob/main/libunwind/src/Registers.hpp#L1905) `setIP`. And within it the SP belongs to the newly created cursor, not the SP belonging to the frame from which `_Unwind_GetIP` originally obtained the instruction pointer. So `setIP` attempts to authenticate an IP that belongs to the original frame using the SP of a different frame resulting in auth failure.
See: https://github.com/rust-lang/backtrace-rs/pull/755 for how this behaviour affects Rust's backtrace-rs.
Contributor guide
Research direction
Start with libunwind/src/UnwindLevel1-gcc-ext.c at _Unwind_FindEnclosingFunction and libunwind/src/Registers.hpp around _Unwind_GetIP, __unw_set_reg, and setIP. Use the AArch64 pointer-authentication scenario described in the issue and the backtrace-rs pull request as a reproducer. Done means the enclosing function can be found for an IP returned from the original unwind frame, with regression coverage for this case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100