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