Branch data Line data Source code
1 : : /* Copyright (C) 2013-2024 Free Software Foundation, Inc.
2 : :
3 : : This file is part of GCC.
4 : :
5 : : GCC is free software; you can redistribute it and/or modify it under
6 : : the terms of the GNU General Public License as published by the Free
7 : : Software Foundation; either version 3, or (at your option) any later
8 : : version.
9 : :
10 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : : for more details.
14 : :
15 : : You should have received a copy of the GNU General Public License
16 : : along with GCC; see the file COPYING3. If not see
17 : : <http://www.gnu.org/licenses/>. */
18 : :
19 : : /* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers
20 : : before using them for virtual method dispatches. */
21 : :
22 : : /* This file is part of the vtable security feature implementation.
23 : : The vtable security feature is designed to detect when a virtual
24 : : call is about to be made through an invalid vtable pointer
25 : : (possibly due to data corruption or malicious attacks). The
26 : : compiler finds every virtual call, and inserts a verification call
27 : : before the virtual call. The verification call takes the actual
28 : : vtable pointer value in the object through which the virtual call
29 : : is being made, and compares the vtable pointer against a set of all
30 : : valid vtable pointers that the object could contain (this set is
31 : : based on the declared type of the object). If the pointer is in
32 : : the valid set, execution is allowed to continue; otherwise the
33 : : program is halted.
34 : :
35 : : There are several pieces needed in order to make this work: 1. For
36 : : every virtual class in the program (i.e. a class that contains
37 : : virtual methods), we need to build the set of all possible valid
38 : : vtables that an object of that class could point to. This includes
39 : : vtables for any class(es) that inherit from the class under
40 : : consideration. 2. For every such data set we build up, we need a
41 : : way to find and reference the data set. This is complicated by the
42 : : fact that the real vtable addresses are not known until runtime,
43 : : when the program is loaded into memory, but we need to reference the
44 : : sets at compile time when we are inserting verification calls into
45 : : the program. 3. We need to find every virtual call in the program,
46 : : and insert the verification call (with the appropriate arguments)
47 : : before the virtual call. 4. We need some runtime library pieces:
48 : : the code to build up the data sets at runtime; the code to actually
49 : : perform the verification using the data sets; and some code to set
50 : : protections on the data sets, so they themselves do not become
51 : : hacker targets.
52 : :
53 : : To find and reference the set of valid vtable pointers for any given
54 : : virtual class, we create a special global variable for each virtual
55 : : class. We refer to this as the "vtable map variable" for that
56 : : class. The vtable map variable has the type "void *", and is
57 : : initialized by the compiler to NULL. At runtime when the set of
58 : : valid vtable pointers for a virtual class, e.g. class Foo, is built,
59 : : the vtable map variable for class Foo is made to point to the set.
60 : : During compile time, when the compiler is inserting verification
61 : : calls into the program, it passes the vtable map variable for the
62 : : appropriate class to the verification call, so that at runtime the
63 : : verification call can find the appropriate data set.
64 : :
65 : : The actual set of valid vtable pointers for a virtual class,
66 : : e.g. class Foo, cannot be built until runtime, when the vtables get
67 : : loaded into memory and their addresses are known. But the knowledge
68 : : about which vtables belong in which class' hierarchy is only known
69 : : at compile time. Therefore at compile time we collect class
70 : : hierarchy and vtable information about every virtual class, and we
71 : : generate calls to build up the data sets at runtime. To build the
72 : : data sets, we call one of the functions we add to the runtime
73 : : library, __VLTRegisterPair. __VLTRegisterPair takes two arguments,
74 : : a vtable map variable and the address of a vtable. If the vtable
75 : : map variable is currently NULL, it creates a new data set (hash
76 : : table), makes the vtable map variable point to the new data set, and
77 : : inserts the vtable address into the data set. If the vtable map
78 : : variable is not NULL, it just inserts the vtable address into the
79 : : data set. In order to make sure that our data sets are built before
80 : : any verification calls happen, we create a special constructor
81 : : initialization function for each compilation unit, give it a very
82 : : high initialization priority, and insert all of our calls to
83 : : __VLTRegisterPair into our special constructor initialization
84 : : function.
85 : :
86 : : The vtable verification feature is controlled by the flag
87 : : '-fvtable-verify='. There are three flavors of this:
88 : : '-fvtable-verify=std', '-fvtable-verify=preinit', and
89 : : '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is
90 : : used, then our constructor initialization function gets put into the
91 : : preinit array. This is necessary if there are data sets that need
92 : : to be built very early in execution. If the constructor
93 : : initialization function gets put into the preinit array, the we also
94 : : add calls to __VLTChangePermission at the beginning and end of the
95 : : function. The call at the beginning sets the permissions on the
96 : : data sets and vtable map variables to read/write, and the one at the
97 : : end makes them read-only. If the '-fvtable-verify=std' option is
98 : : used, the constructor initialization functions are executed at their
99 : : normal time, and the __VLTChangePermission calls are handled
100 : : differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc).
101 : : The option '-fvtable-verify=none' turns off vtable verification.
102 : :
103 : : This file contains code for the tree pass that goes through all the
104 : : statements in each basic block, looking for virtual calls, and
105 : : inserting a call to __VLTVerifyVtablePointer (with appropriate
106 : : arguments) before each one. It also contains the hash table
107 : : functions for the data structures used for collecting the class
108 : : hierarchy data and building/maintaining the vtable map variable data
109 : : are defined in gcc/vtable-verify.h. These data structures are
110 : : shared with the code in the C++ front end that collects the class
111 : : hierarchy & vtable information and generates the vtable map
112 : : variables (see cp/vtable-class-hierarchy.cc). This tree pass should
113 : : run just before the gimple is converted to RTL.
114 : :
115 : : Some implementation details for this pass:
116 : :
117 : : To find all of the virtual calls, we iterate through all the
118 : : gimple statements in each basic block, looking for any call
119 : : statement with the code "OBJ_TYPE_REF". Once we have found the
120 : : virtual call, we need to find the vtable pointer through which the
121 : : call is being made, and the type of the object containing the
122 : : pointer (to find the appropriate vtable map variable). We then use
123 : : these to build a call to __VLTVerifyVtablePointer, passing the
124 : : vtable map variable, and the vtable pointer. We insert the
125 : : verification call just after the gimple statement that gets the
126 : : vtable pointer out of the object, and we update the next
127 : : statement to depend on the result returned from
128 : : __VLTVerifyVtablePointer (the vtable pointer value), to ensure
129 : : subsequent compiler phases don't remove or reorder the call (it's no
130 : : good to have the verification occur after the virtual call, for
131 : : example). To find the vtable pointer being used (and the type of
132 : : the object) we search backwards through the def_stmts chain from the
133 : : virtual call (see verify_bb_vtables for more details). */
134 : :
135 : : #include "config.h"
136 : : #include "system.h"
137 : : #include "coretypes.h"
138 : : #include "backend.h"
139 : : #include "tree.h"
140 : : #include "gimple.h"
141 : : #include "tree-pass.h"
142 : : #include "ssa.h"
143 : : #include "gimple-iterator.h"
144 : :
145 : : #include "vtable-verify.h"
146 : :
147 : : unsigned num_vtable_map_nodes = 0;
148 : : int total_num_virtual_calls = 0;
149 : : int total_num_verified_vcalls = 0;
150 : :
151 : : extern GTY(()) tree verify_vtbl_ptr_fndecl;
152 : : tree verify_vtbl_ptr_fndecl = NULL_TREE;
153 : :
154 : : /* Keep track of whether or not any virtual call were verified. */
155 : : static bool any_verification_calls_generated = false;
156 : :
157 : : unsigned int vtable_verify_main (void);
158 : :
159 : :
160 : : /* The following few functions are for the vtbl pointer hash table
161 : : in the 'registered' field of the struct vtable_map_node. The hash
162 : : table keeps track of which vtable pointers have been used in
163 : : calls to __VLTRegisterPair with that particular vtable map variable. */
164 : :
165 : : /* This function checks to see if a particular VTABLE_DECL and OFFSET are
166 : : already in the 'registered' hash table for NODE. */
167 : :
168 : : bool
169 : 0 : vtbl_map_node_registration_find (struct vtbl_map_node *node,
170 : : tree vtable_decl,
171 : : unsigned offset)
172 : : {
173 : 0 : struct vtable_registration key;
174 : 0 : struct vtable_registration **slot;
175 : :
176 : 0 : gcc_assert (node && node->registered);
177 : :
178 : 0 : key.vtable_decl = vtable_decl;
179 : 0 : slot = node->registered->find_slot (&key, NO_INSERT);
180 : :
181 : 0 : if (slot && (*slot))
182 : : {
183 : : unsigned i;
184 : 0 : for (i = 0; i < ((*slot)->offsets).length (); ++i)
185 : 0 : if ((*slot)->offsets[i] == offset)
186 : : return true;
187 : : }
188 : :
189 : : return false;
190 : : }
191 : :
192 : : /* This function inserts VTABLE_DECL and OFFSET into the 'registered'
193 : : hash table for NODE. It returns a boolean indicating whether or not
194 : : it actually inserted anything. */
195 : :
196 : : bool
197 : 6 : vtbl_map_node_registration_insert (struct vtbl_map_node *node,
198 : : tree vtable_decl,
199 : : unsigned offset)
200 : : {
201 : 6 : struct vtable_registration key;
202 : 6 : struct vtable_registration **slot;
203 : 6 : bool inserted_something = false;
204 : :
205 : 6 : if (!node || !node->registered)
206 : : return false;
207 : :
208 : 6 : key.vtable_decl = vtable_decl;
209 : 6 : slot = node->registered->find_slot (&key, INSERT);
210 : :
211 : 6 : if (! *slot)
212 : : {
213 : 3 : struct vtable_registration *node;
214 : 3 : node = XNEW (struct vtable_registration);
215 : 3 : node->vtable_decl = vtable_decl;
216 : :
217 : 3 : (node->offsets).create (10);
218 : 3 : (node->offsets).safe_push (offset);
219 : 3 : *slot = node;
220 : 3 : inserted_something = true;
221 : : }
222 : : else
223 : : {
224 : : /* We found the vtable_decl slot; we need to see if it already
225 : : contains the offset. If not, we need to add the offset. */
226 : : unsigned i;
227 : : bool found = false;
228 : 6 : for (i = 0; i < ((*slot)->offsets).length () && !found; ++i)
229 : 3 : if ((*slot)->offsets[i] == offset)
230 : 3 : found = true;
231 : :
232 : 3 : if (!found)
233 : : {
234 : 0 : ((*slot)->offsets).safe_push (offset);
235 : 0 : inserted_something = true;
236 : : }
237 : : }
238 : : return inserted_something;
239 : : }
240 : :
241 : : /* Hashtable functions for vtable_registration hashtables. */
242 : :
243 : : inline hashval_t
244 : 7 : registration_hasher::hash (const vtable_registration *p)
245 : : {
246 : 7 : const struct vtable_registration *n = (const struct vtable_registration *) p;
247 : 7 : return (hashval_t) (DECL_UID (n->vtable_decl));
248 : : }
249 : :
250 : : inline bool
251 : 3 : registration_hasher::equal (const vtable_registration *p1,
252 : : const vtable_registration *p2)
253 : : {
254 : 3 : const struct vtable_registration *n1 =
255 : : (const struct vtable_registration *) p1;
256 : 3 : const struct vtable_registration *n2 =
257 : : (const struct vtable_registration *) p2;
258 : 3 : return (DECL_UID (n1->vtable_decl) == DECL_UID (n2->vtable_decl));
259 : : }
260 : :
261 : : /* End of hashtable functions for "registered" hashtables. */
262 : :
263 : :
264 : :
265 : : /* Hashtable definition and functions for vtbl_map_hash. */
266 : :
267 : : struct vtbl_map_hasher : nofree_ptr_hash <struct vtbl_map_node>
268 : : {
269 : : static inline hashval_t hash (const vtbl_map_node *);
270 : : static inline bool equal (const vtbl_map_node *, const vtbl_map_node *);
271 : : };
272 : :
273 : : /* Returns a hash code for P. */
274 : :
275 : : inline hashval_t
276 : 18 : vtbl_map_hasher::hash (const vtbl_map_node *p)
277 : : {
278 : 18 : const struct vtbl_map_node n = *((const struct vtbl_map_node *) p);
279 : 18 : return (hashval_t) IDENTIFIER_HASH_VALUE (n.class_name);
280 : : }
281 : :
282 : : /* Returns nonzero if P1 and P2 are equal. */
283 : :
284 : : inline bool
285 : 6 : vtbl_map_hasher::equal (const vtbl_map_node *p1, const vtbl_map_node *p2)
286 : : {
287 : 6 : const struct vtbl_map_node n1 = *((const struct vtbl_map_node *) p1);
288 : 6 : const struct vtbl_map_node n2 = *((const struct vtbl_map_node *) p2);
289 : 6 : return (IDENTIFIER_HASH_VALUE (n1.class_name) ==
290 : 6 : IDENTIFIER_HASH_VALUE (n2.class_name));
291 : : }
292 : :
293 : : /* Here are the two structures into which we insert vtable map nodes.
294 : : We use two data structures because of the vastly different ways we need
295 : : to find the nodes for various tasks (see comments in vtable-verify.h
296 : : for more details. */
297 : :
298 : : typedef hash_table<vtbl_map_hasher> vtbl_map_table_type;
299 : : typedef vtbl_map_table_type::iterator vtbl_map_iterator_type;
300 : :
301 : : /* Vtable map variable nodes stored in a hash table. */
302 : : static vtbl_map_table_type *vtbl_map_hash;
303 : :
304 : : /* Vtable map variable nodes stored in a vector. */
305 : : vec<struct vtbl_map_node *> vtbl_map_nodes_vec;
306 : :
307 : : /* Vector of mangled names for anonymous classes. */
308 : : extern GTY(()) vec<tree, va_gc> *vtbl_mangled_name_types;
309 : : extern GTY(()) vec<tree, va_gc> *vtbl_mangled_name_ids;
310 : : vec<tree, va_gc> *vtbl_mangled_name_types;
311 : : vec<tree, va_gc> *vtbl_mangled_name_ids;
312 : :
313 : : /* Look up class_type (a type decl for record types) in the vtbl_mangled_names_*
314 : : vectors. This is a linear lookup. Return the associated mangled name for
315 : : the class type. This is for handling types from anonymous namespaces, whose
316 : : DECL_ASSEMBLER_NAME ends up being "<anon>", which is useless for our
317 : : purposes.
318 : :
319 : : We use two vectors of trees to keep track of the mangled names: One is a
320 : : vector of class types and the other is a vector of the mangled names. The
321 : : assumption is that these two vectors are kept in perfect lock-step so that
322 : : vtbl_mangled_name_ids[i] is the mangled name for
323 : : vtbl_mangled_name_types[i]. */
324 : :
325 : : static tree
326 : 0 : vtbl_find_mangled_name (tree class_type)
327 : : {
328 : 0 : tree result = NULL_TREE;
329 : 0 : unsigned i;
330 : :
331 : 0 : if (!vtbl_mangled_name_types or !vtbl_mangled_name_ids)
332 : : return result;
333 : :
334 : 0 : if (vtbl_mangled_name_types->length() != vtbl_mangled_name_ids->length())
335 : : return result;
336 : :
337 : 0 : for (i = 0; i < vtbl_mangled_name_types->length(); ++i)
338 : 0 : if ((*vtbl_mangled_name_types)[i] == class_type)
339 : : {
340 : 0 : result = (*vtbl_mangled_name_ids)[i];
341 : 0 : break;
342 : : }
343 : :
344 : : return result;
345 : : }
346 : :
347 : : /* Store a class type decl and its mangled name, for an anonymous RECORD_TYPE,
348 : : in the vtbl_mangled_names vector. Make sure there is not already an
349 : : entry for the class type before adding it. */
350 : :
351 : : void
352 : 0 : vtbl_register_mangled_name (tree class_type, tree mangled_name)
353 : : {
354 : 0 : if (!vtbl_mangled_name_types)
355 : 0 : vec_alloc (vtbl_mangled_name_types, 10);
356 : :
357 : 0 : if (!vtbl_mangled_name_ids)
358 : 0 : vec_alloc (vtbl_mangled_name_ids, 10);
359 : :
360 : 0 : gcc_assert (vtbl_mangled_name_types->length() ==
361 : : vtbl_mangled_name_ids->length());
362 : :
363 : :
364 : 0 : if (vtbl_find_mangled_name (class_type) == NULL_TREE)
365 : : {
366 : 0 : vec_safe_push (vtbl_mangled_name_types, class_type);
367 : 0 : vec_safe_push (vtbl_mangled_name_ids, mangled_name);
368 : : }
369 : 0 : }
370 : :
371 : : /* Return vtbl_map node for CLASS_NAME without creating a new one. */
372 : :
373 : : struct vtbl_map_node *
374 : 12 : vtbl_map_get_node (tree class_type)
375 : : {
376 : 12 : struct vtbl_map_node key;
377 : 12 : struct vtbl_map_node **slot;
378 : :
379 : 12 : tree class_type_decl;
380 : 12 : tree class_name;
381 : 12 : unsigned int type_quals;
382 : :
383 : 12 : if (!vtbl_map_hash)
384 : : return NULL;
385 : :
386 : 6 : gcc_assert (TREE_CODE (class_type) == RECORD_TYPE);
387 : :
388 : :
389 : : /* Find the TYPE_DECL for the class. */
390 : 6 : class_type_decl = TYPE_NAME (class_type);
391 : :
392 : : /* Verify that there aren't any qualifiers on the type. */
393 : 6 : type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl));
394 : 6 : gcc_assert (type_quals == TYPE_UNQUALIFIED);
395 : :
396 : : /* Get the mangled name for the unqualified type. */
397 : 6 : gcc_assert (HAS_DECL_ASSEMBLER_NAME_P (class_type_decl));
398 : 6 : class_name = DECL_ASSEMBLER_NAME (class_type_decl);
399 : :
400 : 6 : if (strstr (IDENTIFIER_POINTER (class_name), "<anon>") != NULL)
401 : 0 : class_name = vtbl_find_mangled_name (class_type_decl);
402 : :
403 : 6 : key.class_name = class_name;
404 : 6 : slot = (struct vtbl_map_node **) vtbl_map_hash->find_slot (&key, NO_INSERT);
405 : 6 : if (!slot)
406 : : return NULL;
407 : 6 : return *slot;
408 : : }
409 : :
410 : : /* Return vtbl_map node assigned to BASE_CLASS_TYPE. Create new one
411 : : when needed. */
412 : :
413 : : struct vtbl_map_node *
414 : 6 : find_or_create_vtbl_map_node (tree base_class_type)
415 : : {
416 : 6 : struct vtbl_map_node key;
417 : 6 : struct vtbl_map_node *node;
418 : 6 : struct vtbl_map_node **slot;
419 : 6 : tree class_type_decl;
420 : 6 : unsigned int type_quals;
421 : :
422 : 6 : if (!vtbl_map_hash)
423 : 6 : vtbl_map_hash = new vtbl_map_table_type (10);
424 : :
425 : : /* Find the TYPE_DECL for the class. */
426 : 6 : class_type_decl = TYPE_NAME (base_class_type);
427 : :
428 : : /* Verify that there aren't any type qualifiers on type. */
429 : 6 : type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl));
430 : 6 : gcc_assert (type_quals == TYPE_UNQUALIFIED);
431 : :
432 : 6 : gcc_assert (HAS_DECL_ASSEMBLER_NAME_P (class_type_decl));
433 : 6 : key.class_name = DECL_ASSEMBLER_NAME (class_type_decl);
434 : :
435 : 6 : if (strstr (IDENTIFIER_POINTER (key.class_name), "<anon>") != NULL)
436 : 0 : key.class_name = vtbl_find_mangled_name (class_type_decl);
437 : :
438 : 6 : slot = (struct vtbl_map_node **) vtbl_map_hash->find_slot (&key, INSERT);
439 : :
440 : 6 : if (*slot)
441 : : return *slot;
442 : :
443 : 6 : node = XNEW (struct vtbl_map_node);
444 : 6 : node->vtbl_map_decl = NULL_TREE;
445 : 6 : node->class_name = key.class_name;
446 : 6 : node->uid = num_vtable_map_nodes++;
447 : :
448 : 6 : node->class_info = XNEW (struct vtv_graph_node);
449 : 6 : node->class_info->class_type = base_class_type;
450 : 6 : node->class_info->class_uid = node->uid;
451 : 6 : node->class_info->num_processed_children = 0;
452 : :
453 : 6 : (node->class_info->parents).create (4);
454 : 6 : (node->class_info->children).create (4);
455 : :
456 : 6 : node->registered = new register_table_type (16);
457 : :
458 : 6 : node->is_used = false;
459 : :
460 : 6 : vtbl_map_nodes_vec.safe_push (node);
461 : 6 : gcc_assert (vtbl_map_nodes_vec[node->uid] == node);
462 : :
463 : 6 : *slot = node;
464 : 6 : return node;
465 : : }
466 : :
467 : : /* End of hashtable functions for vtable_map variables hash table. */
468 : :
469 : : /* Given a gimple STMT, this function checks to see if the statement
470 : : is an assignment, the rhs of which is getting the vtable pointer
471 : : value out of an object. (i.e. it's the value we need to verify
472 : : because its the vtable pointer that will be used for a virtual
473 : : call). */
474 : :
475 : : static bool
476 : 108 : is_vtable_assignment_stmt (gimple *stmt)
477 : : {
478 : :
479 : 108 : if (gimple_code (stmt) != GIMPLE_ASSIGN)
480 : : return false;
481 : : else
482 : : {
483 : 45 : tree lhs = gimple_assign_lhs (stmt);
484 : 45 : tree rhs = gimple_assign_rhs1 (stmt);
485 : :
486 : 45 : if (TREE_CODE (lhs) != SSA_NAME)
487 : : return false;
488 : :
489 : 39 : if (TREE_CODE (rhs) != COMPONENT_REF)
490 : : return false;
491 : :
492 : 6 : if (! (TREE_OPERAND (rhs, 1))
493 : 6 : || (TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL))
494 : : return false;
495 : :
496 : 6 : if (! DECL_VIRTUAL_P (TREE_OPERAND (rhs, 1)))
497 : 0 : return false;
498 : : }
499 : :
500 : : return true;
501 : : }
502 : :
503 : : /* This function attempts to recover the declared class of an object
504 : : that is used in making a virtual call. We try to get the type from
505 : : the type cast in the gimple assignment statement that extracts the
506 : : vtable pointer from the object (DEF_STMT). The gimple statement
507 : : usually looks something like this:
508 : :
509 : : D.2201_4 = MEM[(struct Event *)this_1(D)]._vptr.Event */
510 : :
511 : : static tree
512 : 0 : extract_object_class_type (tree rhs)
513 : : {
514 : 0 : tree result = NULL_TREE;
515 : :
516 : : /* Try to find and extract the type cast from that stmt. */
517 : 0 : if (TREE_CODE (rhs) == COMPONENT_REF)
518 : : {
519 : 0 : tree op0 = TREE_OPERAND (rhs, 0);
520 : 0 : tree op1 = TREE_OPERAND (rhs, 1);
521 : :
522 : 0 : if (TREE_CODE (op1) == FIELD_DECL
523 : 0 : && DECL_VIRTUAL_P (op1))
524 : : {
525 : 0 : if (TREE_CODE (op0) == COMPONENT_REF
526 : 0 : && TREE_CODE (TREE_OPERAND (op0, 0)) == MEM_REF
527 : 0 : && TREE_CODE (TREE_TYPE (TREE_OPERAND (op0, 0)))== RECORD_TYPE)
528 : 0 : result = TREE_TYPE (TREE_OPERAND (op0, 0));
529 : : else
530 : 0 : result = TREE_TYPE (op0);
531 : : }
532 : 0 : else if (TREE_CODE (op0) == COMPONENT_REF)
533 : : {
534 : 0 : result = extract_object_class_type (op0);
535 : 0 : if (result == NULL_TREE
536 : 0 : && TREE_CODE (op1) == COMPONENT_REF)
537 : : result = extract_object_class_type (op1);
538 : : }
539 : : }
540 : :
541 : 0 : return result;
542 : : }
543 : :
544 : : /* This function traces forward through the def-use chain of an SSA
545 : : variable to see if it ever gets used in a virtual function call. It
546 : : returns a boolean indicating whether or not it found a virtual call in
547 : : the use chain. */
548 : :
549 : : static bool
550 : 12 : var_is_used_for_virtual_call_p (tree lhs, int *mem_ref_depth,
551 : : int *recursion_depth)
552 : : {
553 : 12 : imm_use_iterator imm_iter;
554 : 12 : bool found_vcall = false;
555 : 12 : use_operand_p use_p;
556 : :
557 : 12 : if (TREE_CODE (lhs) != SSA_NAME)
558 : : return false;
559 : :
560 : 12 : if (*mem_ref_depth > 2)
561 : : return false;
562 : :
563 : 12 : if (*recursion_depth > 25)
564 : : /* If we've recursed this far the chances are pretty good that
565 : : we're not going to find what we're looking for, and that we've
566 : : gone down a recursion black hole. Time to stop. */
567 : : return false;
568 : :
569 : 12 : *recursion_depth = *recursion_depth + 1;
570 : :
571 : : /* Iterate through the immediate uses of the current variable. If
572 : : it's a virtual function call, we're done. Otherwise, if there's
573 : : an LHS for the use stmt, add the ssa var to the work list
574 : : (assuming it's not already in the list and is not a variable
575 : : we've already examined. */
576 : :
577 : 18 : FOR_EACH_IMM_USE_FAST (use_p, imm_iter, lhs)
578 : : {
579 : 12 : gimple *stmt2 = USE_STMT (use_p);
580 : :
581 : 12 : if (is_gimple_call (stmt2))
582 : : {
583 : 6 : tree fncall = gimple_call_fn (stmt2);
584 : 6 : if (fncall && TREE_CODE (fncall) == OBJ_TYPE_REF)
585 : : found_vcall = true;
586 : : else
587 : : return false;
588 : : }
589 : 6 : else if (gimple_code (stmt2) == GIMPLE_PHI)
590 : : {
591 : 0 : found_vcall = var_is_used_for_virtual_call_p
592 : 0 : (gimple_phi_result (stmt2),
593 : : mem_ref_depth,
594 : : recursion_depth);
595 : : }
596 : 6 : else if (is_gimple_assign (stmt2))
597 : : {
598 : 6 : tree rhs = gimple_assign_rhs1 (stmt2);
599 : 6 : if (TREE_CODE (rhs) == ADDR_EXPR
600 : 6 : || TREE_CODE (rhs) == MEM_REF)
601 : 0 : *mem_ref_depth = *mem_ref_depth + 1;
602 : :
603 : 6 : if (TREE_CODE (rhs) == COMPONENT_REF)
604 : : {
605 : 0 : while (TREE_CODE (TREE_OPERAND (rhs, 0)) == COMPONENT_REF)
606 : 0 : rhs = TREE_OPERAND (rhs, 0);
607 : :
608 : 0 : if (TREE_CODE (TREE_OPERAND (rhs, 0)) == ADDR_EXPR
609 : 0 : || TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF)
610 : 0 : *mem_ref_depth = *mem_ref_depth + 1;
611 : : }
612 : :
613 : 6 : if (*mem_ref_depth < 3)
614 : 6 : found_vcall = var_is_used_for_virtual_call_p
615 : 6 : (gimple_assign_lhs (stmt2),
616 : : mem_ref_depth,
617 : : recursion_depth);
618 : : }
619 : :
620 : : else
621 : : break;
622 : :
623 : 6 : if (found_vcall)
624 : 0 : return true;
625 : : }
626 : :
627 : : return false;
628 : : }
629 : :
630 : : /* Search through all the statements in a basic block (BB), searching
631 : : for virtual method calls. For each virtual method dispatch, find
632 : : the vptr value used, and the statically declared type of the
633 : : object; retrieve the vtable map variable for the type of the
634 : : object; generate a call to __VLTVerifyVtablePointer; and insert the
635 : : generated call into the basic block, after the point where the vptr
636 : : value is gotten out of the object and before the virtual method
637 : : dispatch. Make the virtual method dispatch depend on the return
638 : : value from the verification call, so that subsequent optimizations
639 : : cannot reorder the two calls. */
640 : :
641 : : static void
642 : 66 : verify_bb_vtables (basic_block bb)
643 : : {
644 : 66 : gimple_seq stmts;
645 : 66 : gimple *stmt = NULL;
646 : 66 : gimple_stmt_iterator gsi_vtbl_assign;
647 : 66 : gimple_stmt_iterator gsi_virtual_call;
648 : :
649 : 66 : stmts = bb_seq (bb);
650 : 66 : gsi_virtual_call = gsi_start (stmts);
651 : 174 : for (; !gsi_end_p (gsi_virtual_call); gsi_next (&gsi_virtual_call))
652 : : {
653 : 108 : stmt = gsi_stmt (gsi_virtual_call);
654 : :
655 : : /* Count virtual calls. */
656 : 108 : if (is_gimple_call (stmt))
657 : : {
658 : 42 : tree fncall = gimple_call_fn (stmt);
659 : 42 : if (fncall && TREE_CODE (fncall) == OBJ_TYPE_REF)
660 : 0 : total_num_virtual_calls++;
661 : : }
662 : :
663 : 108 : if (is_vtable_assignment_stmt (stmt))
664 : : {
665 : 6 : tree lhs = gimple_assign_lhs (stmt);
666 : 6 : tree vtbl_var_decl = NULL_TREE;
667 : 6 : struct vtbl_map_node *vtable_map_node;
668 : 6 : tree vtbl_decl = NULL_TREE;
669 : 6 : gcall *call_stmt;
670 : 6 : const char *vtable_name = "<unknown>";
671 : 6 : tree tmp0;
672 : 6 : bool found;
673 : 6 : int mem_ref_depth = 0;
674 : 6 : int recursion_depth = 0;
675 : :
676 : : /* Make sure this vptr field access is for a virtual call. */
677 : 6 : if (!var_is_used_for_virtual_call_p (lhs, &mem_ref_depth,
678 : : &recursion_depth))
679 : 6 : continue;
680 : :
681 : : /* Now we have found the virtual method dispatch and
682 : : the preceding access of the _vptr.* field... Next
683 : : we need to find the statically declared type of
684 : : the object, so we can find and use the right
685 : : vtable map variable in the verification call. */
686 : 0 : tree class_type = extract_object_class_type
687 : 0 : (gimple_assign_rhs1 (stmt));
688 : :
689 : 0 : gsi_vtbl_assign = gsi_for_stmt (stmt);
690 : :
691 : 0 : if (class_type
692 : 0 : && (TREE_CODE (class_type) == RECORD_TYPE)
693 : 0 : && TYPE_BINFO (class_type))
694 : : {
695 : : /* Get the vtable VAR_DECL for the type. */
696 : 0 : vtbl_var_decl = BINFO_VTABLE (TYPE_BINFO (class_type));
697 : :
698 : 0 : if (TREE_CODE (vtbl_var_decl) == POINTER_PLUS_EXPR)
699 : 0 : vtbl_var_decl = TREE_OPERAND (TREE_OPERAND (vtbl_var_decl, 0),
700 : : 0);
701 : :
702 : 0 : gcc_assert (vtbl_var_decl);
703 : :
704 : 0 : vtbl_decl = vtbl_var_decl;
705 : 0 : vtable_map_node = vtbl_map_get_node
706 : 0 : (TYPE_MAIN_VARIANT (class_type));
707 : :
708 : 0 : gcc_assert (verify_vtbl_ptr_fndecl);
709 : :
710 : : /* Given the vtable pointer for the base class of the
711 : : object, build the call to __VLTVerifyVtablePointer to
712 : : verify that the object's vtable pointer (contained in
713 : : lhs) is in the set of valid vtable pointers for the
714 : : base class. */
715 : :
716 : 0 : if (vtable_map_node && vtable_map_node->vtbl_map_decl)
717 : : {
718 : 0 : vtable_map_node->is_used = true;
719 : 0 : vtbl_var_decl = vtable_map_node->vtbl_map_decl;
720 : :
721 : 0 : if (VAR_P (vtbl_decl))
722 : 0 : vtable_name = IDENTIFIER_POINTER (DECL_NAME (vtbl_decl));
723 : :
724 : : /* Call different routines if we are interested in
725 : : trace information to debug problems. */
726 : 0 : if (flag_vtv_debug)
727 : : {
728 : 0 : call_stmt = gimple_build_call
729 : 0 : (verify_vtbl_ptr_fndecl, 4,
730 : : build1 (ADDR_EXPR,
731 : 0 : TYPE_POINTER_TO
732 : : (TREE_TYPE (vtbl_var_decl)),
733 : : vtbl_var_decl),
734 : : lhs,
735 : : build_string_literal
736 : 0 : (DECL_NAME (vtbl_var_decl)),
737 : : build_string_literal (vtable_name));
738 : : }
739 : : else
740 : 0 : call_stmt = gimple_build_call
741 : 0 : (verify_vtbl_ptr_fndecl, 2,
742 : : build1 (ADDR_EXPR,
743 : 0 : TYPE_POINTER_TO
744 : : (TREE_TYPE (vtbl_var_decl)),
745 : : vtbl_var_decl),
746 : : lhs);
747 : :
748 : :
749 : : /* Create a new SSA_NAME var to hold the call's
750 : : return value, and make the call_stmt use the
751 : : variable for that purpose. */
752 : 0 : tmp0 = make_temp_ssa_name (TREE_TYPE (lhs), NULL, "VTV");
753 : 0 : gimple_call_set_lhs (call_stmt, tmp0);
754 : 0 : update_stmt (call_stmt);
755 : :
756 : : /* Replace all uses of lhs with tmp0. */
757 : 0 : found = false;
758 : 0 : imm_use_iterator iterator;
759 : 0 : gimple *use_stmt;
760 : 0 : FOR_EACH_IMM_USE_STMT (use_stmt, iterator, lhs)
761 : : {
762 : 0 : use_operand_p use_p;
763 : 0 : if (use_stmt == call_stmt)
764 : 0 : continue;
765 : 0 : FOR_EACH_IMM_USE_ON_STMT (use_p, iterator)
766 : 0 : SET_USE (use_p, tmp0);
767 : 0 : update_stmt (use_stmt);
768 : 0 : found = true;
769 : 0 : }
770 : :
771 : 0 : gcc_assert (found);
772 : :
773 : : /* Insert the new verification call just after the
774 : : statement that gets the vtable pointer out of the
775 : : object. */
776 : 0 : gcc_assert (gsi_stmt (gsi_vtbl_assign) == stmt);
777 : 0 : gsi_insert_after (&gsi_vtbl_assign, call_stmt,
778 : : GSI_NEW_STMT);
779 : :
780 : 0 : any_verification_calls_generated = true;
781 : 0 : total_num_verified_vcalls++;
782 : : }
783 : : }
784 : : }
785 : : }
786 : 66 : }
787 : :
788 : : /* Definition of this optimization pass. */
789 : :
790 : : namespace {
791 : :
792 : : const pass_data pass_data_vtable_verify =
793 : : {
794 : : GIMPLE_PASS, /* type */
795 : : "vtable-verify", /* name */
796 : : OPTGROUP_NONE, /* optinfo_flags */
797 : : TV_VTABLE_VERIFICATION, /* tv_id */
798 : : ( PROP_cfg | PROP_ssa ), /* properties_required */
799 : : 0, /* properties_provided */
800 : : 0, /* properties_destroyed */
801 : : 0, /* todo_flags_start */
802 : : TODO_update_ssa, /* todo_flags_finish */
803 : : };
804 : :
805 : : class pass_vtable_verify : public gimple_opt_pass
806 : : {
807 : : public:
808 : 280114 : pass_vtable_verify (gcc::context *ctxt)
809 : 560228 : : gimple_opt_pass (pass_data_vtable_verify, ctxt)
810 : : {}
811 : :
812 : : /* opt_pass methods: */
813 : 1416251 : bool gate (function *) final override { return (flag_vtable_verify); }
814 : : unsigned int execute (function *) final override;
815 : :
816 : : }; // class pass_vtable_verify
817 : :
818 : : /* Loop through all the basic blocks in the current function, passing them to
819 : : verify_bb_vtables, which searches for virtual calls, and inserts
820 : : calls to __VLTVerifyVtablePointer. */
821 : :
822 : : unsigned int
823 : 21 : pass_vtable_verify::execute (function *fun)
824 : : {
825 : 21 : unsigned int ret = 1;
826 : 21 : basic_block bb;
827 : :
828 : 87 : FOR_ALL_BB_FN (bb, fun)
829 : 66 : verify_bb_vtables (bb);
830 : :
831 : 21 : return ret;
832 : : }
833 : :
834 : : } // anon namespace
835 : :
836 : : gimple_opt_pass *
837 : 280114 : make_pass_vtable_verify (gcc::context *ctxt)
838 : : {
839 : 280114 : return new pass_vtable_verify (ctxt);
840 : : }
841 : :
842 : : #include "gt-vtable-verify.h"
|