]>
Commit | Line | Data |
---|---|---|
35228ac7 | 1 | /* UndefinedBehaviorSanitizer, undefined behavior detector. |
8d9254fc | 2 | Copyright (C) 2014-2020 Free Software Foundation, Inc. |
35228ac7 JJ |
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" | |
c7131fb2 | 24 | #include "cp-tree.h" |
35228ac7 | 25 | #include "ubsan.h" |
314e6352 ML |
26 | #include "stringpool.h" |
27 | #include "attribs.h" | |
45b2222a | 28 | #include "asan.h" |
35228ac7 JJ |
29 | |
30 | /* Test if we should instrument vptr access. */ | |
31 | ||
32 | static bool | |
33 | cp_ubsan_instrument_vptr_p (tree type) | |
34 | { | |
35 | if (!flag_rtti || flag_sanitize_undefined_trap_on_error) | |
36 | return false; | |
37 | ||
45b2222a | 38 | if (!sanitize_flags_p (SANITIZE_VPTR)) |
35228ac7 JJ |
39 | return false; |
40 | ||
f34ebeb2 ML |
41 | if (current_function_decl == NULL_TREE) |
42 | return false; | |
43 | ||
35228ac7 JJ |
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 | ||
58 | static tree | |
59 | cp_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) | |
03ca8fb3 JJ |
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 | TREE_NO_WARNING (cond) = 1; | |
85 | vptr = build3_loc (loc, COND_EXPR, uint64_type_node, cond, | |
86 | vptr, build_int_cst (uint64_type_node, 0)); | |
87 | } | |
35228ac7 JJ |
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 | ||
105 | static tree | |
106 | cp_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 | ||
116 | void | |
117 | cp_ubsan_maybe_instrument_member_call (tree stmt) | |
118 | { | |
119 | if (call_expr_nargs (stmt) == 0) | |
120 | return; | |
72af65b9 JM |
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. */ | |
129 | opp = &OBJ_TYPE_REF_EXPR (fn); | |
130 | op = OBJ_TYPE_REF_OBJECT (fn); | |
131 | while (*opp != op) | |
132 | { | |
133 | if (TREE_CODE (*opp) == COMPOUND_EXPR) | |
134 | opp = &TREE_OPERAND (*opp, 1); | |
135 | else | |
136 | opp = &TREE_OPERAND (*opp, 0); | |
137 | } | |
138 | } | |
139 | else | |
35228ac7 | 140 | { |
72af65b9 JM |
141 | /* Non-virtual call: Sanitize the 'this' argument. */ |
142 | opp = &CALL_EXPR_ARG (stmt, 0); | |
143 | if (*opp == error_mark_node | |
144 | || !INDIRECT_TYPE_P (TREE_TYPE (*opp))) | |
145 | return; | |
146 | while (TREE_CODE (*opp) == COMPOUND_EXPR) | |
147 | opp = &TREE_OPERAND (*opp, 1); | |
35228ac7 JJ |
148 | op = *opp; |
149 | } | |
150 | op = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), op, | |
151 | TREE_TYPE (TREE_TYPE (op)), | |
152 | true, UBSAN_MEMBER_CALL); | |
153 | if (op) | |
154 | *opp = op; | |
155 | } | |
156 | ||
157 | /* Data passed to cp_ubsan_check_member_access_r. */ | |
158 | ||
159 | struct cp_ubsan_check_member_access_data | |
160 | { | |
161 | hash_set<tree> *pset; | |
162 | bool is_addr; | |
163 | }; | |
164 | ||
165 | static tree cp_ubsan_check_member_access_r (tree *, int *, void *); | |
166 | ||
167 | /* Instrument a member access. */ | |
168 | ||
169 | static bool | |
170 | cp_ubsan_maybe_instrument_member_access | |
171 | (tree stmt, cp_ubsan_check_member_access_data *ucmd) | |
172 | { | |
173 | if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1))) | |
174 | return false; | |
175 | ||
176 | tree base = TREE_OPERAND (stmt, 0); | |
177 | if (!cp_ubsan_instrument_vptr_p (TREE_TYPE (base))) | |
178 | return false; | |
179 | ||
180 | cp_walk_tree (&base, cp_ubsan_check_member_access_r, ucmd, ucmd->pset); | |
181 | ||
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; | |
188 | } | |
189 | ||
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. */ | |
198 | ||
199 | static tree | |
200 | cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data) | |
201 | { | |
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)) | |
206 | { | |
207 | case ADDR_EXPR: | |
208 | t = TREE_OPERAND (stmt, 0); | |
311bd4d8 | 209 | while ((TREE_CODE (t) == MEM_REF || INDIRECT_REF_P (t)) |
35228ac7 JJ |
210 | && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR) |
211 | t = TREE_OPERAND (TREE_OPERAND (t, 0), 0); | |
212 | if (handled_component_p (t)) | |
213 | { | |
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; | |
219 | } | |
220 | break; | |
221 | case MEM_REF: | |
222 | case INDIRECT_REF: | |
223 | t = TREE_OPERAND (stmt, 0); | |
224 | if (TREE_CODE (t) == ADDR_EXPR) | |
225 | { | |
226 | *walk_subtrees = 0; | |
cd920e13 | 227 | t = TREE_OPERAND (t, 0); |
35228ac7 JJ |
228 | cp_walk_tree (&t, cp_ubsan_check_member_access_r, data, ucmd->pset); |
229 | } | |
230 | break; | |
231 | case COMPONENT_REF: | |
232 | if (!ucmd->is_addr && cp_ubsan_maybe_instrument_member_access (stmt, ucmd)) | |
233 | { | |
234 | *walk_subtrees = 0; | |
235 | break; | |
236 | } | |
237 | /* FALLTHRU */ | |
238 | default: | |
239 | if (ucmd->is_addr && handled_component_p (stmt)) | |
240 | { | |
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++) | |
246 | { | |
247 | cp_walk_tree (&TREE_OPERAND (stmt, i), | |
248 | cp_ubsan_check_member_access_r, data, ucmd->pset); | |
249 | ucmd->is_addr = false; | |
250 | } | |
251 | ucmd->is_addr = true; | |
252 | } | |
253 | break; | |
254 | } | |
255 | return NULL_TREE; | |
256 | } | |
257 | ||
258 | /* Instrument all member accesses inside GENERIC *T_P. */ | |
259 | ||
260 | void | |
261 | cp_ubsan_instrument_member_accesses (tree *t_p) | |
262 | { | |
263 | if (cp_ubsan_instrument_vptr_p (NULL_TREE)) | |
264 | { | |
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); | |
270 | } | |
271 | } | |
272 | ||
273 | /* Instrument downcast. */ | |
274 | ||
275 | tree | |
62775f0d JJ |
276 | cp_ubsan_maybe_instrument_downcast (location_t loc, tree type, |
277 | tree intype, tree op) | |
35228ac7 | 278 | { |
71a93b08 PC |
279 | if (!INDIRECT_TYPE_P (type) |
280 | || !INDIRECT_TYPE_P (intype) | |
281 | || !INDIRECT_TYPE_P (TREE_TYPE (op)) | |
35228ac7 | 282 | || !CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (op))) |
62775f0d | 283 | || !is_properly_derived_from (TREE_TYPE (type), TREE_TYPE (intype))) |
35228ac7 JJ |
284 | return NULL_TREE; |
285 | ||
286 | return cp_ubsan_maybe_instrument_vptr (loc, op, TREE_TYPE (type), true, | |
9f613f06 | 287 | TYPE_PTR_P (type) |
35228ac7 JJ |
288 | ? UBSAN_DOWNCAST_POINTER |
289 | : UBSAN_DOWNCAST_REFERENCE); | |
290 | } | |
291 | ||
292 | /* Instrument cast to virtual base. */ | |
293 | ||
294 | tree | |
295 | cp_ubsan_maybe_instrument_cast_to_vbase (location_t loc, tree type, tree op) | |
296 | { | |
297 | return cp_ubsan_maybe_instrument_vptr (loc, op, type, true, | |
298 | UBSAN_CAST_TO_VBASE); | |
299 | } | |
0c8825de JJ |
300 | |
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. */ | |
304 | ||
305 | static tree | |
306 | cp_ubsan_dfs_initialize_vtbl_ptrs (tree binfo, void *data) | |
307 | { | |
308 | if (!TYPE_CONTAINS_VPTR_P (BINFO_TYPE (binfo))) | |
309 | return dfs_skip_bases; | |
310 | ||
b5a28d80 | 311 | if (!BINFO_PRIMARY_P (binfo)) |
0c8825de JJ |
312 | { |
313 | tree base_ptr = TREE_VALUE ((tree) data); | |
314 | ||
315 | base_ptr = build_base_path (PLUS_EXPR, base_ptr, binfo, /*nonnull=*/1, | |
316 | tf_warning_or_error); | |
317 | ||
318 | /* Compute the location of the vtpr. */ | |
319 | tree vtbl_ptr | |
04757a2a | 320 | = build_vfield_ref (cp_build_fold_indirect_ref (base_ptr), |
0c8825de JJ |
321 | TREE_TYPE (binfo)); |
322 | gcc_assert (vtbl_ptr != error_mark_node); | |
323 | ||
324 | /* Assign NULL to the vptr. */ | |
325 | tree vtbl = build_zero_cst (TREE_TYPE (vtbl_ptr)); | |
4f2e1536 MP |
326 | tree stmt = cp_build_modify_expr (input_location, vtbl_ptr, NOP_EXPR, |
327 | vtbl, tf_warning_or_error); | |
b5a28d80 JM |
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); | |
6651c93e | 332 | finish_expr_stmt (stmt); |
0c8825de JJ |
333 | } |
334 | ||
335 | return NULL_TREE; | |
336 | } | |
337 | ||
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. */ | |
341 | ||
342 | void | |
343 | cp_ubsan_maybe_initialize_vtbl_ptrs (tree addr) | |
344 | { | |
345 | if (!cp_ubsan_instrument_vptr_p (NULL_TREE)) | |
346 | return; | |
347 | ||
348 | tree type = TREE_TYPE (TREE_TYPE (addr)); | |
349 | tree list = build_tree_list (type, addr); | |
1935f250 JJ |
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; | |
0c8825de JJ |
354 | |
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); | |
1935f250 JJ |
359 | |
360 | in_base_initializer = save_in_base_initializer; | |
0c8825de | 361 | } |