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