Line data Source code
1 : /* Copyright (C) 2013-2026 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 6 : registration_hasher::hash (const vtable_registration *p)
245 : {
246 6 : const struct vtable_registration *n = (const struct vtable_registration *) p;
247 6 : 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 106 : is_vtable_assignment_stmt (gimple *stmt)
477 : {
478 :
479 106 : if (gimple_code (stmt) != GIMPLE_ASSIGN)
480 : return false;
481 : else
482 : {
483 49 : tree lhs = gimple_assign_lhs (stmt);
484 49 : tree rhs = gimple_assign_rhs1 (stmt);
485 :
486 49 : if (TREE_CODE (lhs) != SSA_NAME)
487 : return false;
488 :
489 44 : 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 30 : 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 6 : }
626 :
627 6 : 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 57 : verify_bb_vtables (basic_block bb)
643 : {
644 57 : gimple_seq stmts;
645 57 : gimple *stmt = NULL;
646 57 : gimple_stmt_iterator gsi_vtbl_assign;
647 57 : gimple_stmt_iterator gsi_virtual_call;
648 :
649 57 : stmts = bb_seq (bb);
650 57 : gsi_virtual_call = gsi_start (stmts);
651 163 : for (; !gsi_end_p (gsi_virtual_call); gsi_next (&gsi_virtual_call))
652 : {
653 106 : stmt = gsi_stmt (gsi_virtual_call);
654 :
655 : /* Count virtual calls. */
656 106 : if (is_gimple_call (stmt))
657 : {
658 39 : tree fncall = gimple_call_fn (stmt);
659 39 : if (fncall && TREE_CODE (fncall) == OBJ_TYPE_REF)
660 0 : total_num_virtual_calls++;
661 : }
662 :
663 106 : 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 57 : }
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 285722 : pass_vtable_verify (gcc::context *ctxt)
809 571444 : : gimple_opt_pass (pass_data_vtable_verify, ctxt)
810 : {}
811 :
812 : /* opt_pass methods: */
813 1472150 : 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 18 : pass_vtable_verify::execute (function *fun)
824 : {
825 18 : unsigned int ret = 1;
826 18 : basic_block bb;
827 :
828 75 : FOR_ALL_BB_FN (bb, fun)
829 57 : verify_bb_vtables (bb);
830 :
831 18 : return ret;
832 : }
833 :
834 : } // anon namespace
835 :
836 : gimple_opt_pass *
837 285722 : make_pass_vtable_verify (gcc::context *ctxt)
838 : {
839 285722 : return new pass_vtable_verify (ctxt);
840 : }
841 :
842 : #include "gt-vtable-verify.h"
|