From 148827ff4235275f9c9d42c67900fec410caa448 Mon Sep 17 00:00:00 2001 From: Oskar Wirga Date: Tue, 3 Nov 2026 21:34:31 -0500 Subject: [PATCH] Add fcontext support for arm64e (AAPCS/Mach-O) On arm64e, C function pointers are signed with pointer authentication. make_fcontext stored the context function's pointer unchanged, and jump_fcontext branched to it with a plain ret, so the first switch into a new context branched to the still-signed address and crashed. ontop_fcontext had the same problem with the ontop function. On arm64e, make_fcontext now authenticates the pointer and re-signs it with the address of the slot it is stored in, trapping if authentication fails, as clang does. jump_fcontext and ontop_fcontext sign the PC they save the same way, so jump_fcontext can resume any context with braa, and ontop_fcontext branches to the ontop function with braaz. Nothing changes for arm64. Assisted-by: Claude --- src/asm/jump_arm64_aapcs_macho_gas.S | 15 +++++++++++++++ src/asm/make_arm64_aapcs_macho_gas.S | 18 ++++++++++++++++++ src/asm/ontop_arm64_aapcs_macho_gas.S | 13 +++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/asm/jump_arm64_aapcs_macho_gas.S b/src/asm/jump_arm64_aapcs_macho_gas.S index 12cc0215..f836ebbb 100644 --- a/src/asm/jump_arm64_aapcs_macho_gas.S +++ b/src/asm/jump_arm64_aapcs_macho_gas.S @@ -74,7 +74,15 @@ _jump_fcontext: stp fp, lr, [sp, #0x90] ; save LR as PC +#if defined(__arm64e__) + ; signed with the address of its slot + mov x16, lr + add x17, sp, #0xa0 + pacia x16, x17 + str x16, [sp, #0xa0] +#else str lr, [sp, #0xa0] +#endif ; store RSP (pointing to context-data) in X0 mov x4, sp @@ -103,8 +111,15 @@ _jump_fcontext: ; load pc ldr x4, [sp, #0xa0] +#if defined(__arm64e__) + add x17, sp, #0xa0 +#endif ; restore stack from GP + FPU add sp, sp, #0xb0 +#if defined(__arm64e__) + braa x4, x17 +#else ret x4 +#endif diff --git a/src/asm/make_arm64_aapcs_macho_gas.S b/src/asm/make_arm64_aapcs_macho_gas.S index 50f5e824..80de47a4 100644 --- a/src/asm/make_arm64_aapcs_macho_gas.S +++ b/src/asm/make_arm64_aapcs_macho_gas.S @@ -70,7 +70,25 @@ _make_fcontext: ; third arg of make_fcontext() == address of context-function ; store address as a PC to jump in +#if defined(__arm64e__) + ; authenticate the C function pointer (IA, zero discriminator) and sign it + ; with the address of its slot, as jump_fcontext signs a saved PC + mov x16, x2 + autiza x16 + ; trap if authentication failed rather than re-sign a forged pointer, + ; as clang does for arm64e + mov x17, x16 + xpaci x17 + cmp x16, x17 + b.eq 1f + brk #0xc470 +1: + add x17, x0, #0xa0 + pacia x16, x17 + str x16, [x0, #0xa0] +#else str x2, [x0, #0xa0] +#endif adr x1, finish diff --git a/src/asm/ontop_arm64_aapcs_macho_gas.S b/src/asm/ontop_arm64_aapcs_macho_gas.S index 8babe470..00d815cb 100644 --- a/src/asm/ontop_arm64_aapcs_macho_gas.S +++ b/src/asm/ontop_arm64_aapcs_macho_gas.S @@ -74,7 +74,15 @@ _ontop_fcontext: stp x29, x30, [sp, #0x90] ; save LR as PC +#if defined(__arm64e__) + ; signed with the address of its slot, which jump_fcontext authenticates + mov x16, x30 + add x17, sp, #0xa0 + pacia x16, x17 + str x16, [sp, #0xa0] +#else str x30, [sp, #0xa0] +#endif ; store RSP (pointing to context-data) in X5 mov x4, sp @@ -106,4 +114,9 @@ _ontop_fcontext: add sp, sp, #0xb0 ; jump to ontop-function +#if defined(__arm64e__) + ; a C function pointer: IA, zero discriminator + braaz x2 +#else ret x2 +#endif