openmp: Return error_mark_node from tsubst_attribute for errneous varid
[official-gcc.git] / gcc / cp / cp-ubsan.cc
blob1055714fa3571d1334d46de26f4e46bdc031be20
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
10 version.
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.
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
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "cp-tree.h"
26 #include "ubsan.h"
27 #include "stringpool.h"
28 #include "attribs.h"
29 #include "asan.h"
31 /* Test if we should instrument vptr access. */
33 static bool
34 cp_ubsan_instrument_vptr_p (tree type)
36 if (!flag_rtti || (flag_sanitize_trap & SANITIZE_VPTR))
37 return false;
39 if (!sanitize_flags_p (SANITIZE_VPTR))
40 return false;
42 if (current_function_decl == NULL_TREE)
43 return false;
45 if (type)
47 type = TYPE_MAIN_VARIANT (type);
48 if (!CLASS_TYPE_P (type) || !CLASSTYPE_VTABLES (type))
49 return false;
52 return true;
55 /* Helper function for
56 cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
57 Instrument vptr access. */
59 static tree
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)
69 | str_hash2, 64));
70 if (!is_addr)
71 op = build_fold_addr_expr_loc (loc, op);
72 op = save_expr (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);
90 mark_used (ti_decl);
91 tree ptype = build_pointer_type (type);
92 tree call
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
104 NULL_TREE. */
106 static tree
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))
111 return NULL_TREE;
112 return cp_ubsan_instrument_vptr (loc, op, type, is_addr, ckind);
115 /* Instrument a member call (but not constructor call) if needed. */
117 void
118 cp_ubsan_maybe_instrument_member_call (tree stmt)
120 if (call_expr_nargs (stmt) == 0)
121 return;
122 tree op, *opp;
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);
135 else
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)))
141 return;
142 while (TREE_CODE (*opp) == COMPOUND_EXPR)
143 opp = &TREE_OPERAND (*opp, 1);
144 op = *opp;
146 op = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), op,
147 TREE_TYPE (TREE_TYPE (op)),
148 true, UBSAN_MEMBER_CALL);
149 if (!op)
150 /* No change. */;
151 else if (fn && TREE_CODE (fn) == OBJ_TYPE_REF)
152 *opp = cp_build_compound_expr (op, *opp, tf_none);
153 else
154 *opp = op;
157 /* Data passed to cp_ubsan_check_member_access_r. */
159 struct cp_ubsan_check_member_access_data
161 hash_set<tree> *pset;
162 bool is_addr;
165 static tree cp_ubsan_check_member_access_r (tree *, int *, void *);
167 /* Instrument a member access. */
169 static bool
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)))
174 return false;
176 tree base = TREE_OPERAND (stmt, 0);
177 if (!cp_ubsan_instrument_vptr_p (TREE_TYPE (base)))
178 return false;
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);
187 return true;
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
197 not on *x.y. */
199 static tree
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))
207 case ADDR_EXPR:
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))
214 *walk_subtrees = 0;
215 ucmd->is_addr = true;
216 cp_walk_tree (&t, cp_ubsan_check_member_access_r,
217 data, ucmd->pset);
218 ucmd->is_addr = false;
220 break;
221 case MEM_REF:
222 case INDIRECT_REF:
223 t = TREE_OPERAND (stmt, 0);
224 if (TREE_CODE (t) == ADDR_EXPR)
226 *walk_subtrees = 0;
227 t = TREE_OPERAND (t, 0);
228 cp_walk_tree (&t, cp_ubsan_check_member_access_r, data, ucmd->pset);
230 break;
231 case COMPONENT_REF:
232 if (!ucmd->is_addr && cp_ubsan_maybe_instrument_member_access (stmt, ucmd))
234 *walk_subtrees = 0;
235 break;
237 /* FALLTHRU */
238 default:
239 if (ucmd->is_addr && handled_component_p (stmt))
241 int i, len = TREE_OPERAND_LENGTH (stmt);
242 *walk_subtrees = 0;
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;
253 break;
255 return NULL_TREE;
258 /* Instrument all member accesses inside GENERIC *T_P. */
260 void
261 cp_ubsan_instrument_member_accesses (tree *t_p)
263 if (cp_ubsan_instrument_vptr_p (NULL_TREE))
265 hash_set<tree> pset;
266 cp_ubsan_check_member_access_data ucmd;
267 ucmd.pset = &pset;
268 ucmd.is_addr = false;
269 cp_walk_tree (t_p, cp_ubsan_check_member_access_r, &ucmd, &pset);
273 /* Instrument downcast. */
275 tree
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)))
284 return NULL_TREE;
286 return cp_ubsan_maybe_instrument_vptr (loc, op, TREE_TYPE (type), true,
287 TYPE_PTR_P (type)
288 ? UBSAN_DOWNCAST_POINTER
289 : UBSAN_DOWNCAST_REFERENCE);
292 /* Instrument cast to virtual base. */
294 tree
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. */
305 static tree
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. */
319 tree vtbl_ptr
320 = build_vfield_ref (cp_build_fold_indirect_ref (base_ptr),
321 TREE_TYPE (binfo));
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);
335 return NULL_TREE;
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. */
342 void
343 cp_ubsan_maybe_initialize_vtbl_ptrs (tree addr)
345 if (!cp_ubsan_instrument_vptr_p (NULL_TREE))
346 return;
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
351 vtt_parm. */
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
356 class to NULL. */
357 dfs_walk_once (TYPE_BINFO (type), cp_ubsan_dfs_initialize_vtbl_ptrs,
358 NULL, list);
360 in_base_initializer = save_in_base_initializer;