This is an archive of the discontinued LLVM Phabricator instance.

Clear non-addressable bits from fp/sp/lr/pc values in RegisterContextUnwind
ClosedPublic

Authored by jasonmolenda on Jun 13 2023, 2:52 PM.

Details

Summary

The darwin kernel may have authentication bits on fields like $pc or $sp when the register state is thread_get_state'd with macOS Sonoma. debugserver clears these bits before handing the values to lldb arm64 corefiles created by gcore on MacOS Sonoma (macOS 14) will include these signed value as-is.

This patch changes RegisterContextUnwind to clear the unaddressable bits from sp/pc/fp/lr -- these must point to stack or code in memory.

We already clear the bits from spilled lr's because that's frequently signed with an ABI using ARMv8.3 ptrauth. This patch extends that same behavior to sp and fp.

Diff Detail

Event Timeline

jasonmolenda created this revision.Jun 13 2023, 2:52 PM
Herald added a project: Restricted Project. · View Herald Transcript
jasonmolenda requested review of this revision.Jun 13 2023, 2:52 PM

I noticed you're pulling a lot of raw pointers out of shared pointers. Is there a particular reason you're doing that? operator bool on std::shared_ptr returns true if the held pointer is non-null.

I noticed you're pulling a lot of raw pointers out of shared pointers. Is there a particular reason you're doing that? operator bool on std::shared_ptr returns true if the held pointer is non-null.

Nah, I didn't care much in these contexts whether I was holding a shared pointer from the lifetime point of view. You're right it's not going to be more efficient to do it this way, I might as well use the shared pointers I got back instead of extracting the pointer out of the SP.

Address Alex's feedback of not extracting the ABI pointer out of the shared pointer; simply use the shared pointer.

DavidSpickett accepted this revision.Jun 14 2023, 12:57 AM
DavidSpickett added a subscriber: DavidSpickett.

I'm curious how you would end up with a signed PC value, but given this is unwind it could be a value from a previous frame that was signed when stored to the stack.

LGTM.

This revision is now accepted and ready to land.Jun 14 2023, 12:57 AM

I'm curious how you would end up with a signed PC value, but given this is unwind it could be a value from a previous frame that was signed when stored to the stack.

The darwin kernel signs sp/pc (and maybe fp too) when they're at rest inside the kernel, I think. When we fetch the values for these with thread_get_state, they need to be run through and auth-and-clear before the values are sent to lldb (in debugserver aka lldb-server). gcore isn't stripping the auth bits when it fetches the register contexts from the kernel, and is putting those values as-is in corefiles.

You're right that this shouldn't happen in a real process. We already strip auth bits from $lr and spilled $lr's on the stack, where the code actually does sign it. This is purely addressing an artifact of how the darwin kernel represents these internally. gcore should really be clearing the auth bits from these register before putting them in a core file, but we need to work on core files that have already been created like this, so I'm starting with an lldb patch.

This revision was automatically updated to reflect the committed changes.

TIL what gcore is. One can save a corefile from within lldb too, I assume you've checked what that does (and with this change, it wouldn't matter either way).