macovedj opened PR #14303 from macovedj:arm64-native-unwind to bytecodealliance:main:
macOS disables pointer authentication for ordinary arm64 executables. This PR checks the main executable’s Mach-O header and enables return-address signing by default only for arm64e processes.
Previously, Cranelift generated unwind metadata that incorrectly described unsigned return addresses as signed. This caused native backtraces captured inside host imports to stop before recovering the Wasm callers.
The issue was discovered while developing native stack-walking tests for a companion PR that uses libunwind’s whole-section registration APIs.
macovedj requested alexcrichton for a review on PR #14303.
macovedj requested wasmtime-compiler-reviewers for a review on PR #14303.
macovedj requested wasmtime-core-reviewers for a review on PR #14303.
macovedj requested wasmtime-default-reviewers for a review on PR #14303.
macovedj edited PR #14303:
macOS disables pointer authentication for ordinary arm64 executables. This PR checks the main executable’s Mach-O header and enables return-address signing by default only for arm64e processes.
Previously, Cranelift generated unwind metadata that incorrectly described unsigned return addresses as signed. This caused native backtraces captured inside host imports to stop before recovering the Wasm callers.
The issue was discovered while developing native stack-walking tests in https://github.com/bytecodealliance/wasmtime/pull/14304.
github-actions[bot] added the label wasmtime:api on PR #14303.
github-actions[bot] added the label cranelift on PR #14303.
:memo: alexcrichton submitted PR review.
:speech_balloon: alexcrichton created PR review comment:
Question on this, as I'm pretty unfamiliar with this part of macos -- Rust has a
arm64e-apple-darwinin contrast to the normalaarch64-apple-darwintarget. I naively thought that the normal target used pointer authentication everywhere, but I suppose it doesn't and thearm64etarget does? Is it possible to useaarch64-apple-darwinlibraries within anarm64eexecutable? If so, then this check seems required, but if not then another option might be to detect when the target arch isarm64ewhich would probably require a build script (I don't see a #[cfg] in Rust for this)
:speech_balloon: alexcrichton created PR review comment:
Could these features/requirements be dropped? In theory this is a good test for all platforms to execute.
:speech_balloon: alexcrichton created PR review comment:
Could this test use the
backtracecrate directly rather than usnig the unsafe APIs here? That way it should be able to more easily run on other platforms too.
macovedj updated PR #14303.
:memo: macovedj submitted PR review.
:speech_balloon: macovedj created PR review comment:
I’m also getting familiar with this part of macos. From what I can tell, aarch64-apple-darwin uses the ordinary arm64 ABI. It’s also possible to run arm64e binaries with pointer authentication disabled, and when disabled, these targets can mix through dynamically loaded libraries.
This means my original check isn’t sufficient, since it checks the main executable’s architecture rather than whether authentication is actually enabled, so I replaced it with
thread_get_statequery that checks the runtime authentication state.I’m not sure how prevalent these mixed configurations are, although it seems LLVM explicitly documents them, and I was able to reproduce locally. So a build script check for compilation target would definitely miss this case, but if you think that's acceptable then we could still consider it
:memo: alexcrichton submitted PR review.
:speech_balloon: alexcrichton created PR review comment:
It's to not bother with this, most tests in Wasmtime break when features are disabled and we don't try to keep that working. It's assumed the default features are enabled when tests are running
:speech_balloon: alexcrichton created PR review comment:
It's ok to trim this down to just
not(miri), everything else should naturally fall out of the testing matrix
:speech_balloon: alexcrichton created PR review comment:
Ah I forgot to ask from prior, but could this be moved to the
tests/all/*.rssuite?
:speech_balloon: alexcrichton created PR review comment:
Man every time I think I know how pointer authentication works in the "real world" new things come up and turns out I have no idea what's happening...
I can't exactly claim to know whether what you're adding in this PR is correct myself -- would you be able to link to documentation of these constants and/or the APIs here to learn about whether it's dynamically enabled?
macovedj updated PR #14303.
:memo: macovedj submitted PR review.
:speech_balloon: macovedj created PR review comment:
It would be nice if this were documented more clearly. I had to look through XNU, which was relatively gnarly, and I've done my best to concisely and clearly explain what is going on below.
For this plugin-host compatibility rule, the kernel checks whether the executable is an Apple platform binary, along with checking for certain entitlements or an exception list. These include the documented Disable Library Validation entitlement.
For qualifying arm64e hosts, the kernel disables user JOP protection (further reading on JOP available here). The comment explicitly says this accommodates arm64 plugins. This means the main executable’s arm64e architecture alone doesn’t establish whether pointer authentication is enabled.
When exporting thread state, the kernel sets
NO_PTRAUTHif user JOP is disabled for the calling or target thread. This is the info we are querying withthread_get_state(see bindings here). We query our own thread, so the calling and target thread are the same.One of the more confusing details is the field name: the kernel calls it
flags, while the userspace definitions call that same 32-bit field either__opaque_flagsor__pad. Themach2binding uses the__padspelling and#[repr(C)]layout, so that is where we read the flag. There is no separate copy fromflagsinto__pad; they describe the same bytes.The
NO_PTRAUTHconstant is0x1, sostate.__pad & 0x1 == 0checks that this “authentication disabled” bit is clear.Please let me know if there’s any other information or testing I can provide to help build confidence in these changes.
:thumbs_up: alexcrichton submitted PR review.
alexcrichton added PR #14303 Fix native stack-walking on macOS arm64 to the merge queue.
github-merge-queue[bot] removed PR #14303 Fix native stack-walking on macOS arm64 from the merge queue.
macovedj commented on PR #14303:
Ah looking at the CI failure I think I should skip Pulley in my tests
macovedj updated PR #14303.
alexcrichton has enabled auto merge for PR #14303.
alexcrichton added PR #14303 Fix native stack-walking on macOS arm64 to the merge queue.
:check: alexcrichton merged PR #14303.
alexcrichton removed PR #14303 Fix native stack-walking on macOS arm64 from the merge queue.
Last updated: Sep 20 2026 at 18:08 UTC