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