Home | History | Annotate | Line # | Download | only in aarch64
      1 /* Branch Target Identification for AArch64 architecture.
      2    Copyright (C) 2019-2022 Free Software Foundation, Inc.
      3    Contributed by Arm Ltd.
      4 
      5    This file is part of GCC.
      6 
      7    GCC is free software; you can redistribute it and/or modify it
      8    under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3, or (at your option)
     10    any later version.
     11 
     12    GCC is distributed in the hope that it will be useful, but
     13    WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15    General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with GCC; see the file COPYING3.  If not see
     19    <http://www.gnu.org/licenses/>.  */
     20 
     21 #define IN_TARGET_CODE 1
     22 
     23 #include "config.h"
     24 #define INCLUDE_STRING
     25 #include "system.h"
     26 #include "coretypes.h"
     27 #include "backend.h"
     28 #include "target.h"
     29 #include "rtl.h"
     30 #include "tree.h"
     31 #include "memmodel.h"
     32 #include "gimple.h"
     33 #include "tm_p.h"
     34 #include "stringpool.h"
     35 #include "attribs.h"
     36 #include "emit-rtl.h"
     37 #include "gimplify.h"
     38 #include "gimple-iterator.h"
     39 #include "dumpfile.h"
     40 #include "rtl-iter.h"
     41 #include "cfgrtl.h"
     42 #include "tree-pass.h"
     43 #include "cgraph.h"
     44 
     45 /* This pass enables the support for Branch Target Identification Mechanism
     46    for AArch64.  This is a new security feature introduced in ARMv8.5-A
     47    archtitecture.  A BTI instruction is used to guard against the execution
     48    of instructions which are not the intended target of an indirect branch.
     49 
     50    Outside of a guarded memory region, a BTI instruction executes as a NOP.
     51    Within a guarded memory region any target of an indirect branch must be
     52    a compatible BTI or BRK, HLT, PACIASP, PACIBASP instruction (even if the
     53    branch is triggered in a non-guarded memory region).  An incompatibility
     54    generates a Branch Target Exception.
     55 
     56    The compatibility of the BTI instruction is as follows:
     57    BTI j : Can be a target of any indirect jump (BR Xn).
     58    BTI c : Can be a target of any indirect call (BLR Xn and BR X16/X17).
     59    BTI jc: Can be a target of any indirect call or indirect jump.
     60    BTI   : Can not be a target of any indirect call or indirect jump.
     61 
     62   In order to enable this mechanism, this pass iterates through the
     63   control flow of the code and adds appropriate BTI instructions :
     64   * Add a new "BTI C" at the beginning of a function, unless its already
     65     protected by a "PACIASP/PACIBSP".  We exempt the functions that are only
     66     called directly.
     67   * Add a new "BTI J" for every target of an indirect jump, jump table targets,
     68     non-local goto targets or labels that might be referenced by variables,
     69     constant pools, etc (NOTE_INSN_DELETED_LABEL)
     70 
     71   Since we have already changed the use of indirect tail calls to only x16
     72   and x17, we do not have to use "BTI JC".
     73 
     74   This pass is triggered by the command line option -mbranch-protection=bti or
     75   -mbranch-protection=standard.  Since all the BTI instructions are in the HINT
     76   space, this pass does not require any minimum architecture version.  */
     77 
     78 namespace {
     79 
     80 const pass_data pass_data_insert_bti =
     81 {
     82   RTL_PASS, /* type.  */
     83   "bti", /* name.  */
     84   OPTGROUP_NONE, /* optinfo_flags.  */
     85   TV_MACH_DEP, /* tv_id.  */
     86   0, /* properties_required.  */
     87   0, /* properties_provided.  */
     88   0, /* properties_destroyed.  */
     89   0, /* todo_flags_start.  */
     90   0, /* todo_flags_finish.  */
     91 };
     92 
     93 /* Check if X (or any sub-rtx of X) is a PACIASP/PACIBSP instruction.  */
     94 static bool
     95 aarch64_pac_insn_p (rtx x)
     96 {
     97   if (!INSN_P (x))
     98     return false;
     99 
    100   subrtx_var_iterator::array_type array;
    101   FOR_EACH_SUBRTX_VAR (iter, array, PATTERN (x), ALL)
    102     {
    103       rtx sub = *iter;
    104       if (sub && GET_CODE (sub) == UNSPEC)
    105 	{
    106 	  int unspec_val = XINT (sub, 1);
    107 	  switch (unspec_val)
    108 	    {
    109 	    case UNSPEC_PACIASP:
    110             /* fall-through.  */
    111             case UNSPEC_PACIBSP:
    112 	      return true;
    113 
    114 	    default:
    115 	      return false;
    116 	    }
    117 	  iter.skip_subrtxes ();
    118 	}
    119     }
    120   return false;
    121 }
    122 
    123 /* Check if INSN is a BTI J insn.  */
    124 static bool
    125 aarch64_bti_j_insn_p (rtx_insn *insn)
    126 {
    127   if (!insn || !INSN_P (insn))
    128     return false;
    129 
    130   rtx pat = PATTERN (insn);
    131   return GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPECV_BTI_J;
    132 }
    133 
    134 /* Insert the BTI instruction.  */
    135 /* This is implemented as a late RTL pass that runs before branch
    136    shortening and does the following.  */
    137 static unsigned int
    138 rest_of_insert_bti (void)
    139 {
    140   timevar_push (TV_MACH_DEP);
    141 
    142   rtx bti_insn;
    143   rtx_insn *insn;
    144   basic_block bb;
    145 
    146   bb = 0;
    147   FOR_EACH_BB_FN (bb, cfun)
    148     {
    149       for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
    150 	   insn = NEXT_INSN (insn))
    151 	{
    152 	  /* If a label is marked to be preserved or can be a non-local goto
    153 	     target, it must be protected with a BTI J.  */
    154 	  if (LABEL_P (insn)
    155 	       && (LABEL_PRESERVE_P (insn)
    156 		   || bb->flags & BB_NON_LOCAL_GOTO_TARGET))
    157 	    {
    158 	      bti_insn = gen_bti_j ();
    159 	      emit_insn_after (bti_insn, insn);
    160 	      continue;
    161 	    }
    162 
    163 	  /* There could still be more labels that are valid targets of a
    164 	     BTI J instuction.  To find them we start looking through the
    165 	     JUMP_INSN.  If it jumps to a jump table, then we find all labels
    166 	     of the jump table to protect with a BTI J.  */
    167 	  if (JUMP_P (insn))
    168 	    {
    169 	      rtx_jump_table_data *table;
    170 	      if (tablejump_p (insn, NULL, &table))
    171 		{
    172 		  rtvec vec = table->get_labels ();
    173 		  int j;
    174 		  rtx_insn *label;
    175 
    176 		  for (j = GET_NUM_ELEM (vec) - 1; j >= 0; --j)
    177 		    {
    178 		      label = as_a <rtx_insn *> (XEXP (RTVEC_ELT (vec, j), 0));
    179 		      rtx_insn *next = next_nonnote_nondebug_insn (label);
    180 		      if (aarch64_bti_j_insn_p (next))
    181 			continue;
    182 
    183 		      bti_insn = gen_bti_j ();
    184 		      emit_insn_after (bti_insn, label);
    185 		    }
    186 		}
    187 	    }
    188 
    189 	  /* Also look for calls to setjmp () which would be marked with
    190 	     REG_SETJMP note and put a BTI J after.  This is where longjump ()
    191 	     will return.  */
    192 	  if (CALL_P (insn) && (find_reg_note (insn, REG_SETJMP, NULL)))
    193 	    {
    194 	      bti_insn = gen_bti_j ();
    195 	      emit_insn_after (bti_insn, insn);
    196 	      continue;
    197 	    }
    198 	}
    199     }
    200 
    201   /* Since a Branch Target Exception can only be triggered by an indirect call,
    202      we exempt function that are only called directly.  We also exempt
    203      functions that are already protected by Return Address Signing (PACIASP/
    204      PACIBSP).  For all other cases insert a BTI C at the beginning of the
    205      function.  */
    206   if (!cgraph_node::get (cfun->decl)->only_called_directly_p ())
    207     {
    208       bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
    209       insn = BB_HEAD (bb);
    210       if (!aarch64_pac_insn_p (get_first_nonnote_insn ()))
    211 	{
    212 	  bti_insn = gen_bti_c ();
    213 	  emit_insn_before (bti_insn, insn);
    214 	}
    215     }
    216 
    217   timevar_pop (TV_MACH_DEP);
    218   return 0;
    219 }
    220 
    221 
    222 class pass_insert_bti : public rtl_opt_pass
    223 {
    224 public:
    225   pass_insert_bti (gcc::context *ctxt)
    226     : rtl_opt_pass (pass_data_insert_bti, ctxt)
    227   {}
    228 
    229   /* opt_pass methods: */
    230   virtual bool gate (function *)
    231     {
    232       return aarch64_bti_enabled ();
    233     }
    234 
    235   virtual unsigned int execute (function *)
    236     {
    237       return rest_of_insert_bti ();
    238     }
    239 
    240 }; // class pass_insert_bti
    241 
    242 } // anon namespace
    243 
    244 rtl_opt_pass *
    245 make_pass_insert_bti (gcc::context *ctxt)
    246 {
    247   return new pass_insert_bti (ctxt);
    248 }
    249