From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by sourceware.org (Postfix) with ESMTP id C8EA13858D28 for ; Fri, 1 Jul 2022 15:43:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C8EA13858D28 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B846C113E; Fri, 1 Jul 2022 08:43:49 -0700 (PDT) Received: from [10.2.78.56] (unknown [10.2.78.56]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A09243F66F; Fri, 1 Jul 2022 08:43:48 -0700 (PDT) Message-ID: Date: Fri, 1 Jul 2022 16:43:47 +0100 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.9.1 Subject: Re: [PATCH 10/12 V2] arm: Implement cortex-M return signing address codegen Content-Language: en-GB To: Andrea Corallo , Andrea Corallo via Gcc-patches Cc: Richard Earnshaw , nd References: From: Richard Earnshaw In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-3490.7 required=5.0 tests=BAYES_00, KAM_DMARC_STATUS, KAM_LAZY_DOMAIN_SECURITY, KAM_SHORT, NICE_REPLY_A, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 01 Jul 2022 15:43:51 -0000 On 28/06/2022 10:17, Andrea Corallo via Gcc-patches wrote: > Hi all, > > second version of this patch enabling address return signature and > verification based on Armv8.1-M Pointer Authentication [1]. > > To sign the return address, we use the PAC R12, LR, SP instruction > upon function entry. This is signing LR using SP and storing the > result in R12. R12 will be pushed into the stack. > > During function epilogue R12 will be popped and AUT R12, LR, SP will > be used to verify that the content of LR is still valid before return. > > Here an example of PAC instrumented function prologue and epilogue: > > void foo (void); > > int main() > { > foo (); > return 0; > } > > Compiled with '-march=armv8.1-m.main -mbranch-protection=pac-ret > -mthumb' translates into: > > main: > pac ip, lr, sp > push {r3, r7, ip, lr} > add r7, sp, #0 > bl foo > movs r3, #0 > mov r0, r3 > pop {r3, r7, ip, lr} > aut ip, lr, sp > bx lr > > The patch also takes care of generating a PACBTI instruction in place > of the sequence BTI+PAC when Branch Target Identification is enabled > contextually. > > Ex. the previous example compiled with '-march=armv8.1-m.main > -mbranch-protection=pac-ret+bti -mthumb' translates into: > > main: > pacbti ip, lr, sp > push {r3, r7, ip, lr} > add r7, sp, #0 > bl foo > movs r3, #0 > mov r0, r3 > pop {r3, r7, ip, lr} > aut ip, lr, sp > bx lr > > As part of previous upstream suggestions a test for varargs has been > added and '-mtpcs-frame' is deemed being incompatible with this return > signing address feature being introduced. > > [1] > > gcc/Changelog > > * config/arm/arm.c: (arm_compute_frame_layout) > (arm_expand_prologue, thumb2_expand_return, arm_expand_epilogue) > (arm_conditional_register_usage): Update for pac codegen. > (arm_current_function_pac_enabled_p): New function. > * config/arm/arm.md (pac_ip_lr_sp, pacbti_ip_lr_sp, aut_ip_lr_sp): > Add new patterns. > * config/arm/unspecs.md (UNSPEC_PAC_IP_LR_SP) > (UNSPEC_PACBTI_IP_LR_SP, UNSPEC_AUT_IP_LR_SP): Add unspecs. > > gcc/testsuite/Changelog > > * gcc.target/arm/pac.h : New file. > * gcc.target/arm/pac-1.c : New test case. > * gcc.target/arm/pac-2.c : Likewise. > * gcc.target/arm/pac-3.c : Likewise. > * gcc.target/arm/pac-4.c : Likewise. > * gcc.target/arm/pac-5.c : Likewise. > * gcc.target/arm/pac-6.c : Likewise. > * gcc.target/arm/pac-7.c : Likewise. > * gcc.target/arm/pac-8.c : Likewise. > @@ -21139,6 +21139,14 @@ arm_compute_save_core_reg_mask (void) save_reg_mask |= arm_compute_save_reg0_reg12_mask (); + if (arm_current_function_pac_enabled_p ()) + { + if (TARGET_TPCS_FRAME + || (TARGET_TPCS_LEAF_FRAME && crtl->is_leaf)) + error ("TPCS incompatible with return address signing."); + save_reg_mask |= 1 << IP_REGNUM; + } + This is the wrong place for a test like this as it will be generated every time this function is called (which might be more than once per compiled function). However, TPCS frames are only supported for 'thumb-1' code and PACBTI needs armv8-m.main (ie Thumb-2), so the test is really pretty pointless at the moment. I think we should just drop the error. @@ -22302,7 +22310,7 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask) par = emit_insn (par); REG_NOTES (par) = dwarf; - if (!return_in_pc) + if (!return_in_pc && !frame_pointer_needed) arm_add_cfa_adjust_cfa_note (par, UNITS_PER_WORD * num_regs, stack_pointer_rtx, stack_pointer_rtx); } What's this hunk for? It doesn't seem related to the PAC changes. Is this some generic bug? If so, it should be pulled out into a separate patch. If not, it needs some comment as to why we do it this way. @@ -23352,6 +23360,11 @@ output_probe_stack_range (rtx reg1, rtx reg2) return ""; } +static bool aarch_bti_enabled () +{ + return false; +} + GNU style requires the function name to be in the first column: static bool aarch_bti_enabled () { ... @@ -23431,11 +23444,12 @@ arm_expand_prologue (void) /* The static chain register is the same as the IP register. If it is clobbered when creating the frame, we need to save and restore it. */ clobber_ip = IS_NESTED (func_type) - && ((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM) - || ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK - || flag_stack_clash_protection) - && !df_regs_ever_live_p (LR_REGNUM) - && arm_r3_live_at_start_p ())); + && (((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM) + || ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK + || flag_stack_clash_protection) + && !df_regs_ever_live_p (LR_REGNUM) + && arm_r3_live_at_start_p ())) + || (arm_current_function_pac_enabled_p ())); This whole statement needs parenthesis around it so that auto-indent will work properly: clobber_ip = (IS_NESTED (func_type) && (.... || arm_current_function_pac_enabled_p ())); @@ -23511,6 +23525,14 @@ arm_expand_prologue (void) } } + if (arm_current_function_pac_enabled_p ()) + { + if (aarch_bti_enabled ()) + emit_insn (gen_pacbti_nop ()); + else + emit_insn (gen_pac_nop ()); + } What about BTI enabled but PAC not? @@ -27299,7 +27321,8 @@ thumb2_expand_return (bool simple_return) to assert it for now to ensure that future code changes do not silently change this behavior. */ gcc_assert (!IS_CMSE_ENTRY (arm_current_func_type ())); - if (num_regs == 1) + if (num_regs == 1 + && !(arm_current_function_pac_enabled_p ())) Redundant parenthesis. @@ -27314,10 +27337,20 @@ thumb2_expand_return (bool simple_return) } else { - saved_regs_mask &= ~ (1 << LR_REGNUM); - saved_regs_mask |= (1 << PC_REGNUM); - arm_emit_multi_reg_pop (saved_regs_mask); - } + if (arm_current_function_pac_enabled_p ()) + { + saved_regs_mask &= ~ (1 << PC_REGNUM); Is that really needed? The other cases are changing LR to PC, but I don't think PC should already be set as otherwise our calculation of the frame size will be incorrect. Please try it as a gcc_assert() and validate this assertion. + arm_emit_multi_reg_pop (saved_regs_mask); + emit_insn (gen_aut_nop ()); + emit_jump_insn (simple_return_rtx); @@ -30541,6 +30578,9 @@ arm_conditional_register_usage (void) global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1; } + if (TARGET_HAVE_PACBTI) + call_used_regs[IP_REGNUM] = 1; + Why is this needed? CALL_USED_REGISTERS already defines IP (r12) as call-used. +++ b/gcc/config/arm/arm.md @@ -11514,11 +11514,17 @@ (define_expand "prologue" [(clobber (const_int 0))] "TARGET_EITHER" - "if (TARGET_32BIT) + "if (arm_current_function_pac_enabled_p () && !arm_arch8) + { + error (\"This architecture does not support branch protection instructions\"); + DONE; + } No, this is the wrong place for a test like this. A check should be placed in arm_option_override_internal instead (and normally we warn and tweak the options to be something sensible if they conflict). +(define_insn "pac_nop" + [(set (reg:SI IP_REGNUM) + (unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)] + UNSPEC_PAC_NOP))] + "TARGET_THUMB2" + "pac\t%|ip, %|lr, %|sp" + [(set_attr "length" "2")]) This pattern is missing a type. The length is also incorrect as the instruction is 32-bits (4 bytes). Similarly for the other instructions below. Also, you need to mark them as incompatible with conditional execution (they're constrained-unpredictable in IT blocks). All of the tests lack checks that the target board can run PAC/BTI. R.