================ @@ -683,42 +684,139 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) ldp x28,x29, [x0, #0x0E0] #if defined(__ARM_FP) && __ARM_FP != 0 - ldp d0, d1, [x0, #0x110] - ldp d2, d3, [x0, #0x120] - ldp d4, d5, [x0, #0x130] - ldp d6, d7, [x0, #0x140] - ldp d8, d9, [x0, #0x150] - ldp d10,d11, [x0, #0x160] - ldp d12,d13, [x0, #0x170] - ldp d14,d15, [x0, #0x180] - ldp d16,d17, [x0, #0x190] - ldp d18,d19, [x0, #0x1A0] - ldp d20,d21, [x0, #0x1B0] - ldp d22,d23, [x0, #0x1C0] - ldp d24,d25, [x0, #0x1D0] - ldp d26,d27, [x0, #0x1E0] - ldp d28,d29, [x0, #0x1F0] - ldr d30, [x0, #0x200] - ldr d31, [x0, #0x208] + ldp d0, d1, [x0, #0x120] + ldp d2, d3, [x0, #0x130] + ldp d4, d5, [x0, #0x140] + ldp d6, d7, [x0, #0x150] + ldp d8, d9, [x0, #0x160] + ldp d10,d11, [x0, #0x170] + ldp d12,d13, [x0, #0x180] + ldp d14,d15, [x0, #0x190] + ldp d16,d17, [x0, #0x1A0] + ldp d18,d19, [x0, #0x1B0] + ldp d20,d21, [x0, #0x1C0] + ldp d22,d23, [x0, #0x1D0] + ldp d24,d25, [x0, #0x1E0] + ldp d26,d27, [x0, #0x1F0] + ldr d28, [x0, #0x200] + ldr d29, [x0, #0x208] + ldr d30, [x0, #0x210] + ldr d31, [x0, #0x218] #endif + + // The lr value stored in __pc might be signed with IA/IB key (as described + // by __ra_sign.__use_b_key field) and &__pc address as a modifier. + // If the lr value is signed, re-sign that with the corresponding key + // and initial signing scheme using __sp as a modifier and potentially + // involving a second modifier (as described by (__ra_sign.__state & 2)). + + ldr x14, [x0, #0x0F8] // x14 = __sp ---------------- kovdan01 wrote:
This mimics existing behavior of Apple's arm64e and Linux's pauthtest. Particularly, if the pointer is signed, we re-sign that so it is signed with the original signing scheme (rather then with `&__pc` address as a modifier). After that, we authenticate re-signed pointer at return. This is bulky and we can probably do it more elegant. Would be glad to see thoughts of anyone interested regarding this. See my thoughts below. When implementing this, I was focused on maintaining the following three properties of current implementation. 1. We should not do any loads from stack after `sp` is restored (see d5323f6a707e376aab20ebe7864a78fc38fcedaa). 2. The only scratch registers which are allowed to be clobbered are x16 and x17, all other registers are restored from the context structure. 3. Since we are using hint-encoded instructions to maintain compatibility with old hardware, authenticated return is done with regular `ret` following `auti*` instruction. I assumed that we must keep these two instructions together without inserting anything else between them to reduce the time when `lr` remains unsigned. If we could weaken property 2 (e.g. allow x14 and x15 to be clobbered) or property 3 (e.g. allow code between `auti*` and `ret`), we might make the whole implementation much simpler. Particularly, instead of doing a re-sign with almost immediate auth, we might just do one auth. The three limitations listed below do not allow us to have a nice implementation because we need to deal with three conditions for choosing between 5 possible configurations of RA sign state (not signed, signed with IA and no PC modifier, signed with IB and no PC modifier, signed with IA and PC modifier, signed with IB and PC modifier). https://github.com/llvm/llvm-project/pull/171717 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
