]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/cp/cp-ubsan.c
Update copyright years.
[thirdparty/gcc.git] / gcc / cp / cp-ubsan.c
1 /* UndefinedBehaviorSanitizer, undefined behavior detector.
2 Copyright (C) 2014-2017 Free Software Foundation, Inc.
3 Contributed by Jakub Jelinek <jakub@redhat.com>
4
5 This file is part of GCC.
6
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
10 version.
11
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
15 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 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "cp-tree.h"
25 #include "ubsan.h"
26
27 /* Test if we should instrument vptr access. */
28
29 static bool
30 cp_ubsan_instrument_vptr_p (tree type)
31 {
32 if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
33 return false;
34
35 if (!do_ubsan_in_current_function ())
36 return false;
37
38 if (type)
39 {
40 type = TYPE_MAIN_VARIANT (type);
41 if (!CLASS_TYPE_P (type) || !CLASSTYPE_VTABLES (type))
42 return false;
43 }
44
45 return true;
46 }
47
48 /* Helper function for
49 cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
50 Instrument vptr access. */
51
52 static tree
53 cp_ubsan_instrument_vptr (location_t loc, tree op, tree type, bool is_addr,
54 enum ubsan_null_ckind ckind)
55 {
56 type = TYPE_MAIN_VARIANT (type);
57 const char *mangled = mangle_type_string (type);
58 hashval_t str_hash1 = htab_hash_string (mangled);
59 hashval_t str_hash2 = iterative_hash (mangled, strlen (mangled), 0);
60 tree str_hash = wide_int_to_tree (uint64_type_node,
61 wi::uhwi (((uint64_t) str_hash1 << 32)
62 | str_hash2, 64));
63 if (!is_addr)
64 op = build_fold_addr_expr_loc (loc, op);
65 op = save_expr (op);
66 tree vptr = fold_build3_loc (loc, COMPONENT_REF,
67 TREE_TYPE (TYPE_VFIELD (type)),
68 build_fold_indirect_ref_loc (loc, op),
69 TYPE_VFIELD (type), NULL_TREE);
70 vptr = fold_convert_loc (loc, pointer_sized_int_node, vptr);
71 vptr = fold_convert_loc (loc, uint64_type_node, vptr);
72 if (ckind == UBSAN_DOWNCAST_POINTER)
73 {
74 tree cond = build2_loc (loc, NE_EXPR, boolean_type_node, op,
75 build_zero_cst (TREE_TYPE (op)));
76 /* This is a compiler generated comparison, don't emit
77 e.g. -Wnonnull-compare warning for it. */
78 TREE_NO_WARNING (cond) = 1;
79 vptr = build3_loc (loc, COND_EXPR, uint64_type_node, cond,
80 vptr, build_int_cst (uint64_type_node, 0));
81 }
82 tree ti_decl = get_tinfo_decl (type);
83 mark_used (ti_decl);
84 tree ptype = build_pointer_type (type);
85 tree call
86 = build_call_expr_internal_loc (loc, IFN_UBSAN_VPTR,
87 void_type_node, 5, op, vptr, str_hash,
88 build_address (ti_decl),
89 build_int_cst (ptype, ckind));
90 TREE_SIDE_EFFECTS (call) = 1;
91 return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op);
92 }
93
94 /* Helper function for
95 cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
96 Instrument vptr access if it should be instrumented, otherwise return
97 NULL_TREE. */
98
99 static tree
100 cp_ubsan_maybe_instrument_vptr (location_t loc, tree op, tree type,
101 bool is_addr, enum ubsan_null_ckind ckind)
102 {
103 if (!cp_ubsan_instrument_vptr_p (type))
104 return NULL_TREE;
105 return cp_ubsan_instrument_vptr (loc, op, type, is_addr, ckind);
106 }
107
108 /* Instrument a member call (but not constructor call) if needed. */
109
110 void
111 cp_ubsan_maybe_instrument_member_call (tree stmt)
112 {
113 if (call_expr_nargs (stmt) == 0)
114 return;
115 tree *opp = &CALL_EXPR_ARG (stmt, 0);
116 tree op = *opp;
117 if (op == error_mark_node
118 || !POINTER_TYPE_P (TREE_TYPE (op)))
119 return;
120 while (TREE_CODE (op) == COMPOUND_EXPR)
121 {
122 opp = &TREE_OPERAND (op, 1);
123 op = *opp;
124 }
125 op = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), op,
126 TREE_TYPE (TREE_TYPE (op)),
127 true, UBSAN_MEMBER_CALL);
128 if (op)
129 *opp = op;
130 }
131
132 /* Data passed to cp_ubsan_check_member_access_r. */
133
134 struct cp_ubsan_check_member_access_data
135 {
136 hash_set<tree> *pset;
137 bool is_addr;
138 };
139
140 static tree cp_ubsan_check_member_access_r (tree *, int *, void *);
141
142 /* Instrument a member access. */
143
144 static bool
145 cp_ubsan_maybe_instrument_member_access
146 (tree stmt, cp_ubsan_check_member_access_data *ucmd)
147 {
148 if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1)))
149 return false;
150
151 tree base = TREE_OPERAND (stmt, 0);
152 if (!cp_ubsan_instrument_vptr_p (TREE_TYPE (base)))
153 return false;
154
155 cp_walk_tree (&base, cp_ubsan_check_member_access_r, ucmd, ucmd->pset);
156
157 base = cp_ubsan_instrument_vptr (EXPR_LOCATION (stmt), base,
158 TREE_TYPE (base), false,
159 UBSAN_MEMBER_ACCESS);
160 TREE_OPERAND (stmt, 0)
161 = build_fold_indirect_ref_loc (EXPR_LOCATION (stmt), base);
162 return true;
163 }
164
165 /* Attempt to instrument member accesses inside of the function.
166 cp_ubsan_maybe_instrument_member_access should be called on COMPONENT_REFs
167 in the GENERIC IL, but only when the field is actually accessed, not
168 merely when it's address is taken. Therefore we track in is_addr field
169 whether in the current context we are processing address taken
170 handled components or not. E.g. for &x->y[w->z] we want to call
171 cp_ubsan_maybe_instrument_member_access on *w.z COMPONENT_REF, but
172 not on *x.y. */
173
174 static tree
175 cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
176 {
177 tree stmt = *stmt_p, t;
178 cp_ubsan_check_member_access_data *ucmd
179 = (cp_ubsan_check_member_access_data *) data;
180 switch (TREE_CODE (stmt))
181 {
182 case ADDR_EXPR:
183 t = TREE_OPERAND (stmt, 0);
184 while ((TREE_CODE (t) == MEM_REF || INDIRECT_REF_P (t))
185 && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR)
186 t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
187 if (handled_component_p (t))
188 {
189 *walk_subtrees = 0;
190 ucmd->is_addr = true;
191 cp_walk_tree (&t, cp_ubsan_check_member_access_r,
192 data, ucmd->pset);
193 ucmd->is_addr = false;
194 }
195 break;
196 case MEM_REF:
197 case INDIRECT_REF:
198 t = TREE_OPERAND (stmt, 0);
199 if (TREE_CODE (t) == ADDR_EXPR)
200 {
201 *walk_subtrees = 0;
202 t = TREE_OPERAND (stmt, 0);
203 cp_walk_tree (&t, cp_ubsan_check_member_access_r, data, ucmd->pset);
204 }
205 break;
206 case COMPONENT_REF:
207 if (!ucmd->is_addr && cp_ubsan_maybe_instrument_member_access (stmt, ucmd))
208 {
209 *walk_subtrees = 0;
210 break;
211 }
212 /* FALLTHRU */
213 default:
214 if (ucmd->is_addr && handled_component_p (stmt))
215 {
216 int i, len = TREE_OPERAND_LENGTH (stmt);
217 *walk_subtrees = 0;
218 if (!handled_component_p (TREE_OPERAND (stmt, 0)))
219 ucmd->is_addr = false;
220 for (i = 0; i < len; i++)
221 {
222 cp_walk_tree (&TREE_OPERAND (stmt, i),
223 cp_ubsan_check_member_access_r, data, ucmd->pset);
224 ucmd->is_addr = false;
225 }
226 ucmd->is_addr = true;
227 }
228 break;
229 }
230 return NULL_TREE;
231 }
232
233 /* Instrument all member accesses inside GENERIC *T_P. */
234
235 void
236 cp_ubsan_instrument_member_accesses (tree *t_p)
237 {
238 if (cp_ubsan_instrument_vptr_p (NULL_TREE))
239 {
240 hash_set<tree> pset;
241 cp_ubsan_check_member_access_data ucmd;
242 ucmd.pset = &pset;
243 ucmd.is_addr = false;
244 cp_walk_tree (t_p, cp_ubsan_check_member_access_r, &ucmd, &pset);
245 }
246 }
247
248 /* Instrument downcast. */
249
250 tree
251 cp_ubsan_maybe_instrument_downcast (location_t loc, tree type,
252 tree intype, tree op)
253 {
254 if (!POINTER_TYPE_P (type)
255 || !POINTER_TYPE_P (intype)
256 || !POINTER_TYPE_P (TREE_TYPE (op))
257 || !CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (op)))
258 || !is_properly_derived_from (TREE_TYPE (type), TREE_TYPE (intype)))
259 return NULL_TREE;
260
261 return cp_ubsan_maybe_instrument_vptr (loc, op, TREE_TYPE (type), true,
262 TREE_CODE (type) == POINTER_TYPE
263 ? UBSAN_DOWNCAST_POINTER
264 : UBSAN_DOWNCAST_REFERENCE);
265 }
266
267 /* Instrument cast to virtual base. */
268
269 tree
270 cp_ubsan_maybe_instrument_cast_to_vbase (location_t loc, tree type, tree op)
271 {
272 return cp_ubsan_maybe_instrument_vptr (loc, op, type, true,
273 UBSAN_CAST_TO_VBASE);
274 }
275
276 /* Called from initialize_vtbl_ptrs via dfs_walk. BINFO is the base
277 which we want to initialize the vtable pointer for, DATA is
278 TREE_LIST whose TREE_VALUE is the this ptr expression. */
279
280 static tree
281 cp_ubsan_dfs_initialize_vtbl_ptrs (tree binfo, void *data)
282 {
283 if (!TYPE_CONTAINS_VPTR_P (BINFO_TYPE (binfo)))
284 return dfs_skip_bases;
285
286 if (!BINFO_PRIMARY_P (binfo))
287 {
288 tree base_ptr = TREE_VALUE ((tree) data);
289
290 base_ptr = build_base_path (PLUS_EXPR, base_ptr, binfo, /*nonnull=*/1,
291 tf_warning_or_error);
292
293 /* Compute the location of the vtpr. */
294 tree vtbl_ptr
295 = build_vfield_ref (cp_build_indirect_ref (base_ptr, RO_NULL,
296 tf_warning_or_error),
297 TREE_TYPE (binfo));
298 gcc_assert (vtbl_ptr != error_mark_node);
299
300 /* Assign NULL to the vptr. */
301 tree vtbl = build_zero_cst (TREE_TYPE (vtbl_ptr));
302 tree stmt = cp_build_modify_expr (input_location, vtbl_ptr, NOP_EXPR,
303 vtbl, tf_warning_or_error);
304 if (vptr_via_virtual_p (binfo))
305 /* If this vptr comes from a virtual base of the complete object, only
306 clear it if we're in charge of virtual bases. */
307 stmt = build_if_in_charge (stmt);
308 finish_expr_stmt (stmt);
309 }
310
311 return NULL_TREE;
312 }
313
314 /* Initialize all the vtable pointers in the object pointed to by
315 ADDR to NULL, so that we catch invalid calls to methods before
316 mem-initializers are completed. */
317
318 void
319 cp_ubsan_maybe_initialize_vtbl_ptrs (tree addr)
320 {
321 if (!cp_ubsan_instrument_vptr_p (NULL_TREE))
322 return;
323
324 tree type = TREE_TYPE (TREE_TYPE (addr));
325 tree list = build_tree_list (type, addr);
326 /* We cannot rely on the vtable being set up. We have to indirect via the
327 vtt_parm. */
328 int save_in_base_initializer = in_base_initializer;
329 in_base_initializer = 1;
330
331 /* Walk through the hierarchy, initializing the vptr in each base
332 class to NULL. */
333 dfs_walk_once (TYPE_BINFO (type), cp_ubsan_dfs_initialize_vtbl_ptrs,
334 NULL, list);
335
336 in_base_initializer = save_in_base_initializer;
337 }