Home | History | Annotate | Line # | Download | only in cp
cp-ubsan.cc revision 1.1
      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