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