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