1 /* UndefinedBehaviorSanitizer, undefined behavior detector.
2 Copyright (C) 2014-2024 Free Software Foundation, Inc.
3 Contributed by Jakub Jelinek <jakub@redhat.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
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/>. */
21 #define INCLUDE_MEMORY
24 #include "coretypes.h"
27 #include "stringpool.h"
31 /* Test if we should instrument vptr access. */
34 cp_ubsan_instrument_vptr_p (tree type
)
36 if (!flag_rtti
|| (flag_sanitize_trap
& SANITIZE_VPTR
))
39 if (!sanitize_flags_p (SANITIZE_VPTR
))
42 if (current_function_decl
== NULL_TREE
)
47 type
= TYPE_MAIN_VARIANT (type
);
48 if (!CLASS_TYPE_P (type
) || !CLASSTYPE_VTABLES (type
))
55 /* Helper function for
56 cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
57 Instrument vptr access. */
60 cp_ubsan_instrument_vptr (location_t loc
, tree op
, tree type
, bool is_addr
,
61 enum ubsan_null_ckind ckind
)
63 type
= TYPE_MAIN_VARIANT (type
);
64 const char *mangled
= mangle_type_string (type
);
65 hashval_t str_hash1
= htab_hash_string (mangled
);
66 hashval_t str_hash2
= iterative_hash (mangled
, strlen (mangled
), 0);
67 tree str_hash
= wide_int_to_tree (uint64_type_node
,
68 wi::uhwi (((uint64_t) str_hash1
<< 32)
71 op
= build_fold_addr_expr_loc (loc
, op
);
73 tree vptr
= fold_build3_loc (loc
, COMPONENT_REF
,
74 TREE_TYPE (TYPE_VFIELD (type
)),
75 build_fold_indirect_ref_loc (loc
, op
),
76 TYPE_VFIELD (type
), NULL_TREE
);
77 vptr
= fold_convert_loc (loc
, pointer_sized_int_node
, vptr
);
78 vptr
= fold_convert_loc (loc
, uint64_type_node
, vptr
);
79 if (ckind
== UBSAN_DOWNCAST_POINTER
)
81 tree cond
= build2_loc (loc
, NE_EXPR
, boolean_type_node
, op
,
82 build_zero_cst (TREE_TYPE (op
)));
83 /* This is a compiler generated comparison, don't emit
84 e.g. -Wnonnull-compare warning for it. */
85 suppress_warning (cond
, OPT_Wnonnull_compare
);
86 vptr
= build3_loc (loc
, COND_EXPR
, uint64_type_node
, cond
,
87 vptr
, build_int_cst (uint64_type_node
, 0));
89 tree ti_decl
= get_tinfo_decl (type
);
91 tree ptype
= build_pointer_type (type
);
93 = build_call_expr_internal_loc (loc
, IFN_UBSAN_VPTR
,
94 void_type_node
, 5, op
, vptr
, str_hash
,
95 build_address (ti_decl
),
96 build_int_cst (ptype
, ckind
));
97 TREE_SIDE_EFFECTS (call
) = 1;
98 return fold_build2 (COMPOUND_EXPR
, TREE_TYPE (op
), call
, op
);
101 /* Helper function for
102 cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
103 Instrument vptr access if it should be instrumented, otherwise return
107 cp_ubsan_maybe_instrument_vptr (location_t loc
, tree op
, tree type
,
108 bool is_addr
, enum ubsan_null_ckind ckind
)
110 if (!cp_ubsan_instrument_vptr_p (type
))
112 return cp_ubsan_instrument_vptr (loc
, op
, type
, is_addr
, ckind
);
115 /* Instrument a member call (but not constructor call) if needed. */
118 cp_ubsan_maybe_instrument_member_call (tree stmt
)
120 if (call_expr_nargs (stmt
) == 0)
124 tree fn
= CALL_EXPR_FN (stmt
);
125 if (fn
&& TREE_CODE (fn
) == OBJ_TYPE_REF
)
127 /* Virtual function call: Sanitize the use of the object pointer in the
128 OBJ_TYPE_REF, since the vtable reference will SEGV otherwise (95221).
129 OBJ_TYPE_REF_EXPR is ptr->vptr[N] and OBJ_TYPE_REF_OBJECT is ptr. But
130 we can't be sure of finding OBJ_TYPE_REF_OBJECT in OBJ_TYPE_REF_EXPR
131 if the latter has been optimized, so we use a COMPOUND_EXPR below. */
132 opp
= &OBJ_TYPE_REF_EXPR (fn
);
133 op
= OBJ_TYPE_REF_OBJECT (fn
);
137 /* Non-virtual call: Sanitize the 'this' argument. */
138 opp
= &CALL_EXPR_ARG (stmt
, 0);
139 if (*opp
== error_mark_node
140 || !INDIRECT_TYPE_P (TREE_TYPE (*opp
)))
142 while (TREE_CODE (*opp
) == COMPOUND_EXPR
)
143 opp
= &TREE_OPERAND (*opp
, 1);
146 op
= cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt
), op
,
147 TREE_TYPE (TREE_TYPE (op
)),
148 true, UBSAN_MEMBER_CALL
);
151 else if (fn
&& TREE_CODE (fn
) == OBJ_TYPE_REF
)
152 *opp
= cp_build_compound_expr (op
, *opp
, tf_none
);
157 /* Data passed to cp_ubsan_check_member_access_r. */
159 struct cp_ubsan_check_member_access_data
161 hash_set
<tree
> *pset
;
165 static tree
cp_ubsan_check_member_access_r (tree
*, int *, void *);
167 /* Instrument a member access. */
170 cp_ubsan_maybe_instrument_member_access
171 (tree stmt
, cp_ubsan_check_member_access_data
*ucmd
)
173 if (DECL_ARTIFICIAL (TREE_OPERAND (stmt
, 1)))
176 tree base
= TREE_OPERAND (stmt
, 0);
177 if (!cp_ubsan_instrument_vptr_p (TREE_TYPE (base
)))
180 cp_walk_tree (&base
, cp_ubsan_check_member_access_r
, ucmd
, ucmd
->pset
);
182 base
= cp_ubsan_instrument_vptr (EXPR_LOCATION (stmt
), base
,
183 TREE_TYPE (base
), false,
184 UBSAN_MEMBER_ACCESS
);
185 TREE_OPERAND (stmt
, 0)
186 = build_fold_indirect_ref_loc (EXPR_LOCATION (stmt
), base
);
190 /* Attempt to instrument member accesses inside of the function.
191 cp_ubsan_maybe_instrument_member_access should be called on COMPONENT_REFs
192 in the GENERIC IL, but only when the field is actually accessed, not
193 merely when it's address is taken. Therefore we track in is_addr field
194 whether in the current context we are processing address taken
195 handled components or not. E.g. for &x->y[w->z] we want to call
196 cp_ubsan_maybe_instrument_member_access on *w.z COMPONENT_REF, but
200 cp_ubsan_check_member_access_r (tree
*stmt_p
, int *walk_subtrees
, void *data
)
202 tree stmt
= *stmt_p
, t
;
203 cp_ubsan_check_member_access_data
*ucmd
204 = (cp_ubsan_check_member_access_data
*) data
;
205 switch (TREE_CODE (stmt
))
208 t
= TREE_OPERAND (stmt
, 0);
209 while ((TREE_CODE (t
) == MEM_REF
|| INDIRECT_REF_P (t
))
210 && TREE_CODE (TREE_OPERAND (t
, 0)) == ADDR_EXPR
)
211 t
= TREE_OPERAND (TREE_OPERAND (t
, 0), 0);
212 if (handled_component_p (t
))
215 ucmd
->is_addr
= true;
216 cp_walk_tree (&t
, cp_ubsan_check_member_access_r
,
218 ucmd
->is_addr
= false;
223 t
= TREE_OPERAND (stmt
, 0);
224 if (TREE_CODE (t
) == ADDR_EXPR
)
227 t
= TREE_OPERAND (t
, 0);
228 cp_walk_tree (&t
, cp_ubsan_check_member_access_r
, data
, ucmd
->pset
);
232 if (!ucmd
->is_addr
&& cp_ubsan_maybe_instrument_member_access (stmt
, ucmd
))
239 if (ucmd
->is_addr
&& handled_component_p (stmt
))
241 int i
, len
= TREE_OPERAND_LENGTH (stmt
);
243 if (!handled_component_p (TREE_OPERAND (stmt
, 0)))
244 ucmd
->is_addr
= false;
245 for (i
= 0; i
< len
; i
++)
247 cp_walk_tree (&TREE_OPERAND (stmt
, i
),
248 cp_ubsan_check_member_access_r
, data
, ucmd
->pset
);
249 ucmd
->is_addr
= false;
251 ucmd
->is_addr
= true;
258 /* Instrument all member accesses inside GENERIC *T_P. */
261 cp_ubsan_instrument_member_accesses (tree
*t_p
)
263 if (cp_ubsan_instrument_vptr_p (NULL_TREE
))
266 cp_ubsan_check_member_access_data ucmd
;
268 ucmd
.is_addr
= false;
269 cp_walk_tree (t_p
, cp_ubsan_check_member_access_r
, &ucmd
, &pset
);
273 /* Instrument downcast. */
276 cp_ubsan_maybe_instrument_downcast (location_t loc
, tree type
,
277 tree intype
, tree op
)
279 if (!INDIRECT_TYPE_P (type
)
280 || !INDIRECT_TYPE_P (intype
)
281 || !INDIRECT_TYPE_P (TREE_TYPE (op
))
282 || !CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (op
)))
283 || !is_properly_derived_from (TREE_TYPE (type
), TREE_TYPE (intype
)))
286 return cp_ubsan_maybe_instrument_vptr (loc
, op
, TREE_TYPE (type
), true,
288 ? UBSAN_DOWNCAST_POINTER
289 : UBSAN_DOWNCAST_REFERENCE
);
292 /* Instrument cast to virtual base. */
295 cp_ubsan_maybe_instrument_cast_to_vbase (location_t loc
, tree type
, tree op
)
297 return cp_ubsan_maybe_instrument_vptr (loc
, op
, type
, true,
298 UBSAN_CAST_TO_VBASE
);
301 /* Called from initialize_vtbl_ptrs via dfs_walk. BINFO is the base
302 which we want to initialize the vtable pointer for, DATA is
303 TREE_LIST whose TREE_VALUE is the this ptr expression. */
306 cp_ubsan_dfs_initialize_vtbl_ptrs (tree binfo
, void *data
)
308 if (!TYPE_CONTAINS_VPTR_P (BINFO_TYPE (binfo
)))
309 return dfs_skip_bases
;
311 if (!BINFO_PRIMARY_P (binfo
))
313 tree base_ptr
= TREE_VALUE ((tree
) data
);
315 base_ptr
= build_base_path (PLUS_EXPR
, base_ptr
, binfo
, /*nonnull=*/1,
316 tf_warning_or_error
);
318 /* Compute the location of the vtpr. */
320 = build_vfield_ref (cp_build_fold_indirect_ref (base_ptr
),
322 gcc_assert (vtbl_ptr
!= error_mark_node
);
324 /* Assign NULL to the vptr. */
325 tree vtbl
= build_zero_cst (TREE_TYPE (vtbl_ptr
));
326 tree stmt
= cp_build_modify_expr (input_location
, vtbl_ptr
, NOP_EXPR
,
327 vtbl
, tf_warning_or_error
);
328 if (vptr_via_virtual_p (binfo
))
329 /* If this vptr comes from a virtual base of the complete object, only
330 clear it if we're in charge of virtual bases. */
331 stmt
= build_if_in_charge (stmt
);
332 finish_expr_stmt (stmt
);
338 /* Initialize all the vtable pointers in the object pointed to by
339 ADDR to NULL, so that we catch invalid calls to methods before
340 mem-initializers are completed. */
343 cp_ubsan_maybe_initialize_vtbl_ptrs (tree addr
)
345 if (!cp_ubsan_instrument_vptr_p (NULL_TREE
))
348 tree type
= TREE_TYPE (TREE_TYPE (addr
));
349 tree list
= build_tree_list (type
, addr
);
350 /* We cannot rely on the vtable being set up. We have to indirect via the
352 int save_in_base_initializer
= in_base_initializer
;
353 in_base_initializer
= 1;
355 /* Walk through the hierarchy, initializing the vptr in each base
357 dfs_walk_once (TYPE_BINFO (type
), cp_ubsan_dfs_initialize_vtbl_ptrs
,
360 in_base_initializer
= save_in_base_initializer
;