123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- /* UndefinedBehaviorSanitizer, undefined behavior detector.
- Copyright (C) 2014 Free Software Foundation, Inc.
- Contributed by Jakub Jelinek <jakub@redhat.com>
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
- GCC is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
- You should have received a copy of the GNU General Public License
- along with GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>. */
- #include "config.h"
- #include "system.h"
- #include "coretypes.h"
- #include "hash-set.h"
- #include "machmode.h"
- #include "vec.h"
- #include "double-int.h"
- #include "input.h"
- #include "alias.h"
- #include "symtab.h"
- #include "options.h"
- #include "wide-int.h"
- #include "inchash.h"
- #include "tree.h"
- #include "alloc-pool.h"
- #include "output.h"
- #include "toplev.h"
- #include "ubsan.h"
- #include "cp-tree.h"
- #include "c-family/c-common.h"
- #include "c-family/c-ubsan.h"
- #include "asan.h"
- #include "internal-fn.h"
- #include "stor-layout.h"
- #include "builtins.h"
- #include "fold-const.h"
- #include "stringpool.h"
- #include "is-a.h"
- #include "predict.h"
- #include "tree-ssa-alias.h"
- #include "basic-block.h"
- #include "gimple-expr.h"
- #include "gimple.h"
- #include "ipa-ref.h"
- #include "lto-streamer.h"
- #include "cgraph.h"
- /* Test if we should instrument vptr access. */
- static bool
- cp_ubsan_instrument_vptr_p (tree type)
- {
- if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
- return false;
- if (current_function_decl
- && lookup_attribute ("no_sanitize_undefined",
- DECL_ATTRIBUTES (current_function_decl)))
- return false;
- if (type)
- {
- type = TYPE_MAIN_VARIANT (type);
- if (!CLASS_TYPE_P (type) || !CLASSTYPE_VTABLES (type))
- return false;
- }
- return true;
- }
- /* Helper function for
- cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
- Instrument vptr access. */
- static tree
- cp_ubsan_instrument_vptr (location_t loc, tree op, tree type, bool is_addr,
- enum ubsan_null_ckind ckind)
- {
- type = TYPE_MAIN_VARIANT (type);
- const char *mangled = mangle_type_string (type);
- hashval_t str_hash1 = htab_hash_string (mangled);
- hashval_t str_hash2 = iterative_hash (mangled, strlen (mangled), 0);
- tree str_hash = wide_int_to_tree (uint64_type_node,
- wi::uhwi (((uint64_t) str_hash1 << 32)
- | str_hash2, 64));
- if (!is_addr)
- op = build_fold_addr_expr_loc (loc, op);
- op = save_expr (op);
- tree vptr = fold_build3_loc (loc, COMPONENT_REF,
- TREE_TYPE (TYPE_VFIELD (type)),
- build_fold_indirect_ref_loc (loc, op),
- TYPE_VFIELD (type), NULL_TREE);
- vptr = fold_convert_loc (loc, pointer_sized_int_node, vptr);
- vptr = fold_convert_loc (loc, uint64_type_node, vptr);
- if (ckind == UBSAN_DOWNCAST_POINTER)
- vptr = fold_build3 (COND_EXPR, uint64_type_node,
- fold_build2 (NE_EXPR, boolean_type_node, op,
- build_zero_cst (TREE_TYPE (op))),
- vptr, build_int_cst (uint64_type_node, 0));
- tree ti_decl = get_tinfo_decl (type);
- mark_used (ti_decl);
- tree ptype = build_pointer_type (type);
- tree call
- = build_call_expr_internal_loc (loc, IFN_UBSAN_VPTR,
- void_type_node, 5, op, vptr, str_hash,
- build_address (ti_decl),
- build_int_cst (ptype, ckind));
- TREE_SIDE_EFFECTS (call) = 1;
- return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op);
- }
- /* Helper function for
- cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
- Instrument vptr access if it should be instrumented, otherwise return
- NULL_TREE. */
- static tree
- cp_ubsan_maybe_instrument_vptr (location_t loc, tree op, tree type,
- bool is_addr, enum ubsan_null_ckind ckind)
- {
- if (!cp_ubsan_instrument_vptr_p (type))
- return NULL_TREE;
- return cp_ubsan_instrument_vptr (loc, op, type, is_addr, ckind);
- }
- /* Instrument a member call (but not constructor call) if needed. */
- void
- cp_ubsan_maybe_instrument_member_call (tree stmt)
- {
- if (call_expr_nargs (stmt) == 0)
- return;
- tree *opp = &CALL_EXPR_ARG (stmt, 0);
- tree op = *opp;
- if (op == error_mark_node
- || !POINTER_TYPE_P (TREE_TYPE (op)))
- return;
- while (TREE_CODE (op) == COMPOUND_EXPR)
- {
- opp = &TREE_OPERAND (op, 1);
- op = *opp;
- }
- op = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), op,
- TREE_TYPE (TREE_TYPE (op)),
- true, UBSAN_MEMBER_CALL);
- if (op)
- *opp = op;
- }
- /* Data passed to cp_ubsan_check_member_access_r. */
- struct cp_ubsan_check_member_access_data
- {
- hash_set<tree> *pset;
- bool is_addr;
- };
- static tree cp_ubsan_check_member_access_r (tree *, int *, void *);
- /* Instrument a member access. */
- static bool
- cp_ubsan_maybe_instrument_member_access
- (tree stmt, cp_ubsan_check_member_access_data *ucmd)
- {
- if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1)))
- return false;
- tree base = TREE_OPERAND (stmt, 0);
- if (!cp_ubsan_instrument_vptr_p (TREE_TYPE (base)))
- return false;
- cp_walk_tree (&base, cp_ubsan_check_member_access_r, ucmd, ucmd->pset);
- base = cp_ubsan_instrument_vptr (EXPR_LOCATION (stmt), base,
- TREE_TYPE (base), false,
- UBSAN_MEMBER_ACCESS);
- TREE_OPERAND (stmt, 0)
- = build_fold_indirect_ref_loc (EXPR_LOCATION (stmt), base);
- return true;
- }
- /* Attempt to instrument member accesses inside of the function.
- cp_ubsan_maybe_instrument_member_access should be called on COMPONENT_REFs
- in the GENERIC IL, but only when the field is actually accessed, not
- merely when it's address is taken. Therefore we track in is_addr field
- whether in the current context we are processing address taken
- handled components or not. E.g. for &x->y[w->z] we want to call
- cp_ubsan_maybe_instrument_member_access on *w.z COMPONENT_REF, but
- not on *x.y. */
- static tree
- cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
- {
- tree stmt = *stmt_p, t;
- cp_ubsan_check_member_access_data *ucmd
- = (cp_ubsan_check_member_access_data *) data;
- switch (TREE_CODE (stmt))
- {
- case ADDR_EXPR:
- t = TREE_OPERAND (stmt, 0);
- while ((TREE_CODE (t) == MEM_REF || TREE_CODE (t) == INDIRECT_REF)
- && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR)
- t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
- if (handled_component_p (t))
- {
- *walk_subtrees = 0;
- ucmd->is_addr = true;
- cp_walk_tree (&t, cp_ubsan_check_member_access_r,
- data, ucmd->pset);
- ucmd->is_addr = false;
- }
- break;
- case MEM_REF:
- case INDIRECT_REF:
- t = TREE_OPERAND (stmt, 0);
- if (TREE_CODE (t) == ADDR_EXPR)
- {
- *walk_subtrees = 0;
- t = TREE_OPERAND (stmt, 0);
- cp_walk_tree (&t, cp_ubsan_check_member_access_r, data, ucmd->pset);
- }
- break;
- case COMPONENT_REF:
- if (!ucmd->is_addr && cp_ubsan_maybe_instrument_member_access (stmt, ucmd))
- {
- *walk_subtrees = 0;
- break;
- }
- /* FALLTHRU */
- default:
- if (ucmd->is_addr && handled_component_p (stmt))
- {
- int i, len = TREE_OPERAND_LENGTH (stmt);
- *walk_subtrees = 0;
- if (!handled_component_p (TREE_OPERAND (stmt, 0)))
- ucmd->is_addr = false;
- for (i = 0; i < len; i++)
- {
- cp_walk_tree (&TREE_OPERAND (stmt, i),
- cp_ubsan_check_member_access_r, data, ucmd->pset);
- ucmd->is_addr = false;
- }
- ucmd->is_addr = true;
- }
- break;
- }
- return NULL_TREE;
- }
- /* Instrument all member accesses inside GENERIC *T_P. */
- void
- cp_ubsan_instrument_member_accesses (tree *t_p)
- {
- if (cp_ubsan_instrument_vptr_p (NULL_TREE))
- {
- hash_set<tree> pset;
- cp_ubsan_check_member_access_data ucmd;
- ucmd.pset = &pset;
- ucmd.is_addr = false;
- cp_walk_tree (t_p, cp_ubsan_check_member_access_r, &ucmd, &pset);
- }
- }
- /* Instrument downcast. */
- tree
- cp_ubsan_maybe_instrument_downcast (location_t loc, tree type, tree op)
- {
- if (!POINTER_TYPE_P (type)
- || !POINTER_TYPE_P (TREE_TYPE (op))
- || !CLASS_TYPE_P (TREE_TYPE (type))
- || !CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (op)))
- || !DERIVED_FROM_P (TREE_TYPE (TREE_TYPE (op)), TREE_TYPE (type)))
- return NULL_TREE;
- return cp_ubsan_maybe_instrument_vptr (loc, op, TREE_TYPE (type), true,
- TREE_CODE (type) == POINTER_TYPE
- ? UBSAN_DOWNCAST_POINTER
- : UBSAN_DOWNCAST_REFERENCE);
- }
- /* Instrument cast to virtual base. */
- tree
- cp_ubsan_maybe_instrument_cast_to_vbase (location_t loc, tree type, tree op)
- {
- return cp_ubsan_maybe_instrument_vptr (loc, op, type, true,
- UBSAN_CAST_TO_VBASE);
- }
|