1 /* Branch Target Identification for AArch64 architecture.
2    Copyright (C) 2019 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
aarch64_pac_insn_p(rtx x)95 aarch64_pac_insn_p (rtx x)
96 {
97   if (!INSN_P (x))
98     return x;
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_PACISP:
110 	      return true;
111 
112 	    default:
113 	      return false;
114 	    }
115 	  iter.skip_subrtxes ();
116 	}
117     }
118   return false;
119 }
120 
121 /* Insert the BTI instruction.  */
122 /* This is implemented as a late RTL pass that runs before branch
123    shortening and does the following.  */
124 static unsigned int
rest_of_insert_bti(void)125 rest_of_insert_bti (void)
126 {
127   timevar_push (TV_MACH_DEP);
128 
129   rtx bti_insn;
130   rtx_insn *insn;
131   basic_block bb;
132 
133   /* Since a Branch Target Exception can only be triggered by an indirect call,
134      we exempt function that are only called directly.  We also exempt
135      functions that are already protected by Return Address Signing (PACIASP/
136      PACIBSP).  For all other cases insert a BTI C at the beginning of the
137      function.  */
138   if (!cgraph_node::get (cfun->decl)->only_called_directly_p ())
139     {
140       bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
141       insn = BB_HEAD (bb);
142       if (!aarch64_pac_insn_p (get_first_nonnote_insn ()))
143 	{
144 	  bti_insn = gen_bti_c ();
145 	  emit_insn_before (bti_insn, insn);
146 	}
147     }
148 
149   bb = 0;
150   FOR_EACH_BB_FN (bb, cfun)
151     {
152       for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
153 	   insn = NEXT_INSN (insn))
154 	{
155 	  /* If a label is marked to be preserved or can be a non-local goto
156 	     target, it must be protected with a BTI J.  The same applies to
157 	     NOTE_INSN_DELETED_LABEL since they are basically labels that might
158 	     be referenced via variables or constant pool.  */
159 	  if ((LABEL_P (insn)
160 	       && (LABEL_PRESERVE_P (insn)
161 		   || bb->flags & BB_NON_LOCAL_GOTO_TARGET))
162 	      || (NOTE_P (insn)
163 		  && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL))
164 	    {
165 	      bti_insn = gen_bti_j ();
166 	      emit_insn_after (bti_insn, insn);
167 	      continue;
168 	    }
169 
170 	  /* There could still be more labels that are valid targets of a
171 	     BTI J instuction.  To find them we start looking through the
172 	     JUMP_INSN.  If it jumps to a jump table, then we find all labels
173 	     of the jump table to protect with a BTI J.  */
174 	  if (JUMP_P (insn))
175 	    {
176 	      rtx_jump_table_data *table;
177 	      if (tablejump_p (insn, NULL, &table))
178 		{
179 		  rtvec vec = table->get_labels ();
180 		  int j;
181 		  rtx_insn *label;
182 
183 		  for (j = GET_NUM_ELEM (vec) - 1; j >= 0; --j)
184 		    {
185 		      label = as_a <rtx_insn *> (XEXP (RTVEC_ELT (vec, j), 0));
186 		      bti_insn = gen_bti_j ();
187 		      emit_insn_after (bti_insn, label);
188 		    }
189 		}
190 	    }
191 
192 	  /* Also look for calls to setjmp () which would be marked with
193 	     REG_SETJMP note and put a BTI J after.  This is where longjump ()
194 	     will return.  */
195 	  if (CALL_P (insn) && (find_reg_note (insn, REG_SETJMP, NULL)))
196 	    {
197 	      bti_insn = gen_bti_j ();
198 	      emit_insn_after (bti_insn, insn);
199 	      continue;
200 	    }
201 	}
202     }
203 
204   timevar_pop (TV_MACH_DEP);
205   return 0;
206 }
207 
208 
209 class pass_insert_bti : public rtl_opt_pass
210 {
211 public:
pass_insert_bti(gcc::context * ctxt)212   pass_insert_bti (gcc::context *ctxt)
213     : rtl_opt_pass (pass_data_insert_bti, ctxt)
214   {}
215 
216   /* opt_pass methods: */
gate(function *)217   virtual bool gate (function *)
218     {
219       return aarch64_bti_enabled ();
220     }
221 
execute(function *)222   virtual unsigned int execute (function *)
223     {
224       return rest_of_insert_bti ();
225     }
226 
227 }; // class pass_insert_bti
228 
229 } // anon namespace
230 
231 rtl_opt_pass *
make_pass_insert_bti(gcc::context * ctxt)232 make_pass_insert_bti (gcc::context *ctxt)
233 {
234   return new pass_insert_bti (ctxt);
235 }
236