https://github.com/libffi/libffi/commit/45d284f2d066cc3a080c5be88e51b4d934349797 From 45d284f2d066cc3a080c5be88e51b4d934349797 Mon Sep 17 00:00:00 2001 From: Bill Roberts <152999275+billatarm@users.noreply.github.com> Date: Sat, 1 Jun 2024 12:34:53 -0500 Subject: [PATCH] aarch64: support pointer authentication (#834) * aarch64: fix callstack in ffi_call_SYSV The debug stack gets corrupted between the frame and stack pivots, update the CFI directives so the call stack stays correct in the debugger. str x9, [x1, #32] // stack is ffi_call_SYSV() -> ffi_call_int() -> ffi_call_int() -> main() (good) mov x29, x1 // stack is ffi_call_SYSV() -> ffi_call_int() -> ffi_call_int() -> ffi_call() -> main() (bad) mov sp, x0 // stack is ffi_call_SYSV() -> ffi_call_int() -> ffi_call_int() -> main() (good) The CFA data needs to be updated around the pivots, after this patch the callstack stays correct. Signed-off-by: Bill Roberts * aarch64: remove uneeded CFI directive This directive doesn't actually set the CFA to anything valid, and during unwinding this isn't even used. Note that the PAC/Darwin usage is quite suspect as well, as the CFA is either x1 or x29 after the frame pivot, and the CFA address is what's used as the modifier when verifying the PAC. At least this is the behavior on Linux with PAC, I need to verify ARME ABI unwinding. So for now leave Darwin as is. Signed-off-by: Bill Roberts * ptrauth: rename define for clarity Rename the HAVE_PTRAUTH define for clarity that its associated with the ARM64E ABI and not the ARM64 ABI that can be supported on Linux and enabled with -mbranch-protection=standard. Signed-off-by: Bill Roberts * aarch64: add PAC support to ffi_call_SYSV Support AARCH64 Pointer Authentication Codes (PAC) within ffi_call_SYSV and support exception unwinding. The Linux ABI for PAC is to use paciasp/autiasp instructions which also have hint space equivelent instructions. They sign the LR (x30) with the A key and the current stack pointer as the salt. Note that this can also be configured to use the B key and will use pacibsp/autibsp hint instructions. The Linux ABI for exception frame data when PAC is enabled assumes that the Connonical Frame Address, or CFA is equal to the stack pointer. I.E sp is equal to x29 (fp). When the unwinder is invoked the cfa will point to the frame which will include the *signed* return address from the LR. This will then be passed to __builtin_aarch64_autia1716 where the CFA will be used as the salt and stored to register x16 and register x17 will contain the signed address to demangle. This can be noted in: - https://github.com/gcc-mirror/gcc/blob/d6d7afcdbc04adb0ec42a44b2d7e05600945af42/libgcc/config/aarch64/aarch64-unwind.h#L56 The other required portion of this is to indicate to the unwinder that this is a signed address that needs to go the special demangle route in the unwinder. This is accomplished by using CFI directive "cfi_window_save" which marks that frame as being signed. Putting all of this together is a bit tricky, as the internals of ffi_call_SYSV the callee allocates its stack and frame and passes it in arg1 (x0) and arg2 (x1) to the called function, where that function pivots its stack, so care must be taken to get the sp == fp before paciasp is called and also restore that state before autiasp is called. Signed-off-by: Bill Roberts --------- Signed-off-by: Bill Roberts --- configure.ac | 6 ++-- include/ffi_cfi.h | 2 ++ src/aarch64/ffi.c | 4 +-- src/aarch64/internal.h | 76 ++++++++++++++++++++++++++++++++---------- src/aarch64/sysv.S | 20 ++++++----- src/closures.c | 6 ++-- 6 files changed, 81 insertions(+), 33 deletions(-) diff --git a/configure.ac b/configure.ac index 816bfd666..b35a999fb 100644 --- a/configure.ac +++ b/configure.ac @@ -189,17 +189,17 @@ AC_CACHE_CHECK([whether compiler supports pointer authentication], AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ #ifdef __clang__ # if __has_feature(ptrauth_calls) -# define HAVE_PTRAUTH 1 +# define HAVE_ARM64E_PTRAUTH 1 # endif #endif -#ifndef HAVE_PTRAUTH +#ifndef HAVE_ARM64E_PTRAUTH # error Pointer authentication not supported #endif ]])],[libffi_cv_as_ptrauth=yes],[libffi_cv_as_ptrauth=no]) ]) if test "x$libffi_cv_as_ptrauth" = xyes; then - AC_DEFINE(HAVE_PTRAUTH, 1, + AC_DEFINE(HAVE_ARM64E_PTRAUTH, 1, [Define if your compiler supports pointer authentication.]) fi diff --git a/include/ffi_cfi.h b/include/ffi_cfi.h index f4c292d00..856566324 100644 --- a/include/ffi_cfi.h +++ b/include/ffi_cfi.h @@ -49,6 +49,7 @@ # define cfi_personality(enc, exp) .cfi_personality enc, exp # define cfi_lsda(enc, exp) .cfi_lsda enc, exp # define cfi_escape(...) .cfi_escape __VA_ARGS__ +# define cfi_window_save .cfi_window_save #else @@ -71,6 +72,7 @@ # define cfi_personality(enc, exp) # define cfi_lsda(enc, exp) # define cfi_escape(...) +# define cfi_window_save #endif /* HAVE_AS_CFI_PSEUDO_OP */ #endif /* FFI_CFI_H */ diff --git a/src/aarch64/ffi.c b/src/aarch64/ffi.c index b13738e38..964934dfb 100644 --- a/src/aarch64/ffi.c +++ b/src/aarch64/ffi.c @@ -63,7 +63,7 @@ struct call_context #if FFI_EXEC_TRAMPOLINE_TABLE #ifdef __MACH__ -#ifdef HAVE_PTRAUTH +#ifdef HAVE_ARM64E_PTRAUTH #include #endif #include @@ -877,7 +877,7 @@ ffi_prep_closure_loc (ffi_closure *closure, #if FFI_EXEC_TRAMPOLINE_TABLE # ifdef __MACH__ -# ifdef HAVE_PTRAUTH +# ifdef HAVE_ARM64E_PTRAUTH codeloc = ptrauth_auth_data(codeloc, ptrauth_key_function_pointer, 0); # endif void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE); diff --git a/src/aarch64/internal.h b/src/aarch64/internal.h index b5d102b4a..c39f9cb22 100644 --- a/src/aarch64/internal.h +++ b/src/aarch64/internal.h @@ -81,20 +81,62 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Helpers for writing assembly compatible with arm ptr auth */ #ifdef LIBFFI_ASM -#ifdef HAVE_PTRAUTH -#define SIGN_LR pacibsp -#define SIGN_LR_WITH_REG(x) pacib lr, x -#define AUTH_LR_AND_RET retab -#define AUTH_LR_WITH_REG(x) autib lr, x -#define BRANCH_AND_LINK_TO_REG blraaz -#define BRANCH_TO_REG braaz -#else -#define SIGN_LR -#define SIGN_LR_WITH_REG(x) -#define AUTH_LR_AND_RET ret -#define AUTH_LR_WITH_REG(x) -#define BRANCH_AND_LINK_TO_REG blr -#define BRANCH_TO_REG br -#endif - -#endif + #if defined(HAVE_ARM64E_PTRAUTH) + /* ARM64E ABI For Darwin */ + #define SIGN_LR pacibsp + #define SIGN_LR_WITH_REG(x) pacib lr, x + #define AUTH_LR_AND_RET retab + #define AUTH_LR_WITH_REG(x) autib lr, x + #define BRANCH_AND_LINK_TO_REG blraaz + #define BRANCH_TO_REG braaz + #define PAC_CFI_WINDOW_SAVE + /* Linux PAC Support */ + #elif defined(__ARM_FEATURE_PAC_DEFAULT) + #define GNU_PROPERTY_AARCH64_POINTER_AUTH (1 << 1) + #define PAC_CFI_WINDOW_SAVE cfi_window_save + #define TMP_REG x9 + #define BRANCH_TO_REG br + #define BRANCH_AND_LINK_TO_REG blr + #define SIGN_LR_LINUX_ONLY SIGN_LR + /* Which key to sign with? */ + #if (__ARM_FEATURE_PAC_DEFAULT & 1) == 1 + /* Signed with A-key */ + #define SIGN_LR hint #25 /* paciasp */ + #define AUTH_LR hint #29 /* autiasp */ + #else + /* Signed with B-key */ + #define SIGN_LR hint #27 /* pacibsp */ + #define AUTH_LR hint #31 /* autibsp */ + #endif /* __ARM_FEATURE_PAC_DEFAULT */ + #define AUTH_LR_WITH_REG(x) _auth_lr_with_reg x +.macro _auth_lr_with_reg modifier + mov TMP_REG, sp + mov sp, \modifier + AUTH_LR + mov sp, TMP_REG +.endm + #define SIGN_LR_WITH_REG(x) _sign_lr_with_reg x +.macro _sign_lr_with_reg modifier + mov TMP_REG, sp + mov sp, \modifier + SIGN_LR + mov sp, TMP_REG +.endm + #define AUTH_LR_AND_RET _auth_lr_and_ret modifier +.macro _auth_lr_and_ret modifier + AUTH_LR + ret +.endm + #undef TMP_REG + + /* No Pointer Auth */ + #else + #define SIGN_LR + #define SIGN_LR_WITH_REG(x) + #define AUTH_LR_AND_RET ret + #define AUTH_LR_WITH_REG(x) + #define BRANCH_AND_LINK_TO_REG blr + #define BRANCH_TO_REG br + #define PAC_CFI_WINDOW_SAVE + #endif /* HAVE_ARM64E_PTRAUTH */ +#endif /* LIBFFI_ASM */ diff --git a/src/aarch64/sysv.S b/src/aarch64/sysv.S index 60cfa505b..6a9a5611f 100644 --- a/src/aarch64/sysv.S +++ b/src/aarch64/sysv.S @@ -92,27 +92,27 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ cfi_startproc CNAME(ffi_call_SYSV): BTI_C - /* Sign the lr with x1 since that is where it will be stored */ + PAC_CFI_WINDOW_SAVE + /* Sign the lr with x1 since that is the CFA which is the modifer used in auth instructions */ SIGN_LR_WITH_REG(x1) - /* Use a stack frame allocated by our caller. */ -#if defined(HAVE_PTRAUTH) && defined(__APPLE__) +#if defined(HAVE_ARM64E_PTRAUTH) && defined(__APPLE__) /* darwin's libunwind assumes that the cfa is the sp and that's the data * used to sign the lr. In order to allow unwinding through this * function it is necessary to point the cfa at the signing register. */ cfi_def_cfa(x1, 0); -#else - cfi_def_cfa(x1, 40); #endif + /* Use a stack frame allocated by our caller. */ stp x29, x30, [x1] + cfi_def_cfa_register(x1) + cfi_rel_offset (x29, 0) + cfi_rel_offset (x30, 8) mov x9, sp str x9, [x1, #32] mov x29, x1 - mov sp, x0 cfi_def_cfa_register(x29) - cfi_rel_offset (x29, 0) - cfi_rel_offset (x30, 8) + mov sp, x0 mov x9, x2 /* save fn */ mov x8, x3 /* install structure return */ @@ -326,6 +326,7 @@ CNAME(ffi_closure_SYSV_V): cfi_startproc BTI_C SIGN_LR + PAC_CFI_WINDOW_SAVE stp x29, x30, [sp, #-ffi_closure_SYSV_FS]! cfi_adjust_cfa_offset (ffi_closure_SYSV_FS) cfi_rel_offset (x29, 0) @@ -351,6 +352,7 @@ CNAME(ffi_closure_SYSV_V): CNAME(ffi_closure_SYSV): BTI_C SIGN_LR + PAC_CFI_WINDOW_SAVE stp x29, x30, [sp, #-ffi_closure_SYSV_FS]! cfi_adjust_cfa_offset (ffi_closure_SYSV_FS) cfi_rel_offset (x29, 0) @@ -648,6 +650,8 @@ CNAME(ffi_go_closure_SYSV_V): cfi_startproc CNAME(ffi_go_closure_SYSV): BTI_C + SIGN_LR_LINUX_ONLY + PAC_CFI_WINDOW_SAVE stp x29, x30, [sp, #-ffi_closure_SYSV_FS]! cfi_adjust_cfa_offset (ffi_closure_SYSV_FS) cfi_rel_offset (x29, 0) diff --git a/src/closures.c b/src/closures.c index 67a94a822..02cf78fa2 100644 --- a/src/closures.c +++ b/src/closures.c @@ -164,7 +164,7 @@ ffi_tramp_is_present (__attribute__((unused)) void *ptr) #include #include -#ifdef HAVE_PTRAUTH +#ifdef HAVE_ARM64E_PTRAUTH #include #endif #include @@ -223,7 +223,7 @@ ffi_trampoline_table_alloc (void) /* Remap the trampoline table on top of the placeholder page */ trampoline_page = config_page + PAGE_MAX_SIZE; -#ifdef HAVE_PTRAUTH +#ifdef HAVE_ARM64E_PTRAUTH trampoline_page_template = (vm_address_t)(uintptr_t)ptrauth_auth_data((void *)&ffi_closure_trampoline_table_page, ptrauth_key_function_pointer, 0); #else trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page; @@ -268,7 +268,7 @@ ffi_trampoline_table_alloc (void) ffi_trampoline_table_entry *entry = &table->free_list_pool[i]; entry->trampoline = (void *) (trampoline_page + (i * FFI_TRAMPOLINE_SIZE)); -#ifdef HAVE_PTRAUTH +#ifdef HAVE_ARM64E_PTRAUTH entry->trampoline = ptrauth_sign_unauthenticated(entry->trampoline, ptrauth_key_function_pointer, 0); #endif