[Lldb-commits] [lldb] [lldb] Introduce Process::FixAnyAddressPreservingAuthentication (PR #159785)
Jason Molenda via lldb-commits
lldb-commits at lists.llvm.org
Sun Sep 21 20:44:45 PDT 2025
jasonmolenda wrote:
A little bit of background, I think everyone understands the basic idea of what Felipe is doing here & why, but I think outlining where we are could be helpful.
AArch64 processes can be run with ptrauth enabled (the "arm64e" slice, the ABI only officially announced as stable in this year's OS releases, and now usable by third parties). Things like function pointers are signed against a private key in the cpu, things like the link register are signed agaInst a private key _and_ a discriminator of the stack pointer value on function entry. In the function epilogue, $sp is restored to its original value and the link register is validated against the secret key and $sp. The Darwin AArch64 processors run in Top Byte Ignore mode, which means metadata can be stored in the top byte without breaking load/store instructions. Function addresses put PAC signing in the top byte, data access signing leaves the top byte user-controlled.
As an aside, we have a pass (not upstreamed) for expressions that will sign things like function pointers in jitted expressions. If you do `p (int)qsort(start, count, elemsize, comparefunc)` in lldb, we will find the address of `comparefunc()` in the symbol table and pass that in to the expression. But this is a function pointer that the arm64e ABI requires be PAC signed. We can't sign it in lldb, the signing must execute in-process to have the correct keys. So there's a static initializer pass inserted into expressions that signs these. These global ABI-mandated PAC signed values aren't signed against a discriminator, just the internal key, so we can do it fine. (you could imagine a scenario where someone might try to construct a 64-bit representation of the argument types for a function, and use that as a discriminator. but I don't know if anyone has actually done that.)
Apple also introduced Memory Integrity Enforcement in this year's releases, and on the new iPhones one part of MIE is the AArch64 Memory Tag Extension on the new iPhones. When a process opts in to MIE and MTE, 4 bits of the top byte for some heap allocates are now used to store an MTE tag which validates that a pointer cannot access beyond its allocated range.
With swift async funclets, called asynchronously, they are passed a pointer to the swift async context block. This is a heap allocated object, and so in process with MIE enabled on an MTE-capable device, it will have an MTE tag. lldb can read and write memory from the swift async context without preserving the MTE tag because its own target-memory accesses aren't authenticated against the tags. But when we pass the swift async context address into a jitted expression -- so the address is being used in code running in the process -- now we must include the MTE tag in the value sent into the expression.
An additional wrinkle is that it seems that in the swift funclet, when built with PAC (arm64e), the local register value pointing to a variable in the swift async context will be PAC signed against a local discriminator value. The user does an expression which uses this variable, but the jitted code isn't aware that the swift async funclet is PAC-enforcing the address of this variable, it just thinks it's using an address of the variable. So we must strip the PAC bits, but it's an address in the heap-allocated MTE swift async context, so we must also retain the MTE tag in the top byte.
We considered "well, what if we just leave the PAC bits in there" -- but normal load/store instructions (the ones that do not check PAC signing) require that all non-address bits outside of the Top Byte (with TBI mode enabled) are either 0's or 1's. Anything else in those bit ranges results in a fault. We can have arbitrary data in the top byte, but the rest of the non-address bits must be correct.
https://github.com/llvm/llvm-project/pull/159785
More information about the lldb-commits
mailing list