LCOV - code coverage report
Current view: top level - gcc/cp - cp-ubsan.cc (source / functions) Hit Total Coverage
Test: gcc.info Lines: 146 147 99.3 %
Date: 2023-07-19 08:18:47 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /* UndefinedBehaviorSanitizer, undefined behavior detector.
       2             :    Copyright (C) 2014-2023 Free Software Foundation, Inc.
       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"
      24             : #include "cp-tree.h"
      25             : #include "ubsan.h"
      26             : #include "stringpool.h"
      27             : #include "attribs.h"
      28             : #include "asan.h"
      29             : 
      30             : /* Test if we should instrument vptr access.  */
      31             : 
      32             : static bool
      33        8700 : cp_ubsan_instrument_vptr_p (tree type)
      34             : {
      35        8700 :   if (!flag_rtti || (flag_sanitize_trap & SANITIZE_VPTR))
      36             :     return false;
      37             : 
      38        8580 :   if (!sanitize_flags_p (SANITIZE_VPTR))
      39             :     return false;
      40             : 
      41        8580 :   if (current_function_decl == NULL_TREE)
      42             :     return false;
      43             : 
      44        8580 :   if (type)
      45             :     {
      46        3972 :       type = TYPE_MAIN_VARIANT (type);
      47        3972 :       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        1316 : cp_ubsan_instrument_vptr (location_t loc, tree op, tree type, bool is_addr,
      60             :                           enum ubsan_null_ckind ckind)
      61             : {
      62        1316 :   type = TYPE_MAIN_VARIANT (type);
      63        1316 :   const char *mangled = mangle_type_string (type);
      64        1316 :   hashval_t str_hash1 = htab_hash_string (mangled);
      65        1316 :   hashval_t str_hash2 = iterative_hash (mangled, strlen (mangled), 0);
      66        1316 :   tree str_hash = wide_int_to_tree (uint64_type_node,
      67        2632 :                                     wi::uhwi (((uint64_t) str_hash1 << 32)
      68        1316 :                                               | str_hash2, 64));
      69        1316 :   if (!is_addr)
      70         388 :     op = build_fold_addr_expr_loc (loc, op);
      71        1316 :   op = save_expr (op);
      72        2632 :   tree vptr = fold_build3_loc (loc, COMPONENT_REF,
      73        1316 :                                TREE_TYPE (TYPE_VFIELD (type)),
      74             :                                build_fold_indirect_ref_loc (loc, op),
      75        1316 :                                TYPE_VFIELD (type), NULL_TREE);
      76        1316 :   vptr = fold_convert_loc (loc, pointer_sized_int_node, vptr);
      77        1316 :   vptr = fold_convert_loc (loc, uint64_type_node, vptr);
      78        1316 :   if (ckind == UBSAN_DOWNCAST_POINTER)
      79             :     {
      80          51 :       tree cond = build2_loc (loc, NE_EXPR, boolean_type_node, op,
      81          51 :                               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          51 :       suppress_warning (cond, OPT_Wnonnull_compare);
      85          51 :       vptr = build3_loc (loc, COND_EXPR, uint64_type_node, cond,
      86          51 :                          vptr, build_int_cst (uint64_type_node, 0));
      87             :     }
      88        1316 :   tree ti_decl = get_tinfo_decl (type);
      89        1316 :   mark_used (ti_decl);
      90        1316 :   tree ptype = build_pointer_type (type);
      91        1316 :   tree call
      92        1316 :     = 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        1316 :   TREE_SIDE_EFFECTS (call) = 1;
      97        1316 :   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        2527 : cp_ubsan_maybe_instrument_vptr (location_t loc, tree op, tree type,
     107             :                                 bool is_addr, enum ubsan_null_ckind ckind)
     108             : {
     109        2527 :   if (!cp_ubsan_instrument_vptr_p (type))
     110             :     return NULL_TREE;
     111         928 :   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        2436 : cp_ubsan_maybe_instrument_member_call (tree stmt)
     118             : {
     119        2436 :   if (call_expr_nargs (stmt) == 0)
     120             :     return;
     121        2436 :   tree op, *opp;
     122             : 
     123        2436 :   tree fn = CALL_EXPR_FN (stmt);
     124        2436 :   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.  But
     129             :          we can't be sure of finding OBJ_TYPE_REF_OBJECT in OBJ_TYPE_REF_EXPR
     130             :          if the latter has been optimized, so we use a COMPOUND_EXPR below.  */
     131         114 :       opp = &OBJ_TYPE_REF_EXPR (fn);
     132         114 :       op = OBJ_TYPE_REF_OBJECT (fn);
     133             :     }
     134             :   else
     135             :     {
     136             :       /* Non-virtual call: Sanitize the 'this' argument.  */
     137        2322 :       opp = &CALL_EXPR_ARG (stmt, 0);
     138        2322 :       if (*opp == error_mark_node
     139        2322 :           || !INDIRECT_TYPE_P (TREE_TYPE (*opp)))
     140             :         return;
     141        3806 :       while (TREE_CODE (*opp) == COMPOUND_EXPR)
     142        1484 :         opp = &TREE_OPERAND (*opp, 1);
     143             :       op = *opp;
     144             :     }
     145        2436 :   op = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), op,
     146        2436 :                                        TREE_TYPE (TREE_TYPE (op)),
     147             :                                        true, UBSAN_MEMBER_CALL);
     148        2436 :   if (!op)
     149             :     /* No change.  */;
     150         837 :   else if (fn && TREE_CODE (fn) == OBJ_TYPE_REF)
     151         114 :     *opp = cp_build_compound_expr (op, *opp, tf_none);
     152             :   else
     153         723 :     *opp = op;
     154             : }
     155             : 
     156             : /* Data passed to cp_ubsan_check_member_access_r.  */
     157             : 
     158             : struct cp_ubsan_check_member_access_data
     159             : {
     160             :   hash_set<tree> *pset;
     161             :   bool is_addr;
     162             : };
     163             : 
     164             : static tree cp_ubsan_check_member_access_r (tree *, int *, void *);
     165             : 
     166             : /* Instrument a member access.  */
     167             : 
     168             : static bool
     169        4199 : cp_ubsan_maybe_instrument_member_access
     170             :      (tree stmt, cp_ubsan_check_member_access_data *ucmd)
     171             : {
     172        4199 :   if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1)))
     173             :     return false;
     174             : 
     175        1445 :   tree base = TREE_OPERAND (stmt, 0);
     176        1445 :   if (!cp_ubsan_instrument_vptr_p (TREE_TYPE (base)))
     177             :     return false;
     178             : 
     179         388 :   cp_walk_tree (&base, cp_ubsan_check_member_access_r, ucmd, ucmd->pset);
     180             : 
     181         388 :   base = cp_ubsan_instrument_vptr (EXPR_LOCATION (stmt), base,
     182         388 :                                    TREE_TYPE (base), false,
     183             :                                    UBSAN_MEMBER_ACCESS);
     184         388 :   TREE_OPERAND (stmt, 0)
     185         388 :     = build_fold_indirect_ref_loc (EXPR_LOCATION (stmt), base);
     186         388 :   return true;
     187             : }
     188             : 
     189             : /* Attempt to instrument member accesses inside of the function.
     190             :    cp_ubsan_maybe_instrument_member_access should be called on COMPONENT_REFs
     191             :    in the GENERIC IL, but only when the field is actually accessed, not
     192             :    merely when it's address is taken.  Therefore we track in is_addr field
     193             :    whether in the current context we are processing address taken
     194             :    handled components or not.  E.g. for &x->y[w->z] we want to call
     195             :    cp_ubsan_maybe_instrument_member_access on *w.z COMPONENT_REF, but
     196             :    not on *x.y.  */
     197             : 
     198             : static tree
     199      131490 : cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
     200             : {
     201      131490 :   tree stmt = *stmt_p, t;
     202      131490 :   cp_ubsan_check_member_access_data *ucmd
     203             :     = (cp_ubsan_check_member_access_data *) data;
     204      131490 :   switch (TREE_CODE (stmt))
     205             :     {
     206       10380 :     case ADDR_EXPR:
     207       10380 :       t = TREE_OPERAND (stmt, 0);
     208       10380 :       while ((TREE_CODE (t) == MEM_REF || INDIRECT_REF_P (t))
     209       10380 :              && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR)
     210           0 :         t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
     211       10380 :       if (handled_component_p (t))
     212             :         {
     213         965 :           *walk_subtrees = 0;
     214         965 :           ucmd->is_addr = true;
     215         965 :           cp_walk_tree (&t, cp_ubsan_check_member_access_r,
     216             :                         data, ucmd->pset);
     217         965 :           ucmd->is_addr = false;
     218             :         }
     219             :       break;
     220        4429 :     case MEM_REF:
     221        4429 :     case INDIRECT_REF:
     222        4429 :       t = TREE_OPERAND (stmt, 0);
     223        4429 :       if (TREE_CODE (t) == ADDR_EXPR)
     224             :         {
     225          21 :           *walk_subtrees = 0;
     226          21 :           t = TREE_OPERAND (t, 0);
     227          21 :           cp_walk_tree (&t, cp_ubsan_check_member_access_r, data, ucmd->pset);
     228             :         }
     229             :       break;
     230        5321 :     case COMPONENT_REF:
     231        5321 :       if (!ucmd->is_addr && cp_ubsan_maybe_instrument_member_access (stmt, ucmd))
     232             :         {
     233         388 :           *walk_subtrees = 0;
     234         388 :           break;
     235             :         }
     236             :       /* FALLTHRU */
     237      116293 :     default:
     238      116293 :       if (ucmd->is_addr && handled_component_p (stmt))
     239             :         {
     240        1146 :           int i, len = TREE_OPERAND_LENGTH (stmt);
     241        1146 :           *walk_subtrees = 0;
     242        1146 :           if (!handled_component_p (TREE_OPERAND (stmt, 0)))
     243         961 :             ucmd->is_addr = false;
     244        4608 :           for (i = 0; i < len; i++)
     245             :             {
     246        3462 :               cp_walk_tree (&TREE_OPERAND (stmt, i),
     247             :                             cp_ubsan_check_member_access_r, data, ucmd->pset);
     248        3462 :               ucmd->is_addr = false;
     249             :             }
     250        1146 :           ucmd->is_addr = true;
     251             :         }
     252             :       break;
     253             :     }
     254      131490 :   return NULL_TREE;
     255             : }
     256             : 
     257             : /* Instrument all member accesses inside GENERIC *T_P.  */
     258             : 
     259             : void
     260        4296 : cp_ubsan_instrument_member_accesses (tree *t_p)
     261             : {
     262        4296 :   if (cp_ubsan_instrument_vptr_p (NULL_TREE))
     263             :     {
     264        4176 :       hash_set<tree> pset;
     265        4176 :       cp_ubsan_check_member_access_data ucmd;
     266        4176 :       ucmd.pset = &pset;
     267        4176 :       ucmd.is_addr = false;
     268        4176 :       cp_walk_tree (t_p, cp_ubsan_check_member_access_r, &ucmd, &pset);
     269        4176 :     }
     270        4296 : }
     271             : 
     272             : /* Instrument downcast.  */
     273             : 
     274             : tree
     275         191 : cp_ubsan_maybe_instrument_downcast (location_t loc, tree type,
     276             :                                     tree intype, tree op)
     277             : {
     278         191 :   if (!INDIRECT_TYPE_P (type)
     279         191 :       || !INDIRECT_TYPE_P (intype)
     280          51 :       || !INDIRECT_TYPE_P (TREE_TYPE (op))
     281          51 :       || !CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (op)))
     282         242 :       || !is_properly_derived_from (TREE_TYPE (type), TREE_TYPE (intype)))
     283         140 :     return NULL_TREE;
     284             : 
     285          51 :   return cp_ubsan_maybe_instrument_vptr (loc, op, TREE_TYPE (type), true,
     286          51 :                                          TYPE_PTR_P (type)
     287             :                                          ? UBSAN_DOWNCAST_POINTER
     288          51 :                                          : UBSAN_DOWNCAST_REFERENCE);
     289             : }
     290             : 
     291             : /* Instrument cast to virtual base.  */
     292             : 
     293             : tree
     294          40 : cp_ubsan_maybe_instrument_cast_to_vbase (location_t loc, tree type, tree op)
     295             : {
     296          40 :   return cp_ubsan_maybe_instrument_vptr (loc, op, type, true,
     297          40 :                                          UBSAN_CAST_TO_VBASE);
     298             : }
     299             : 
     300             : /* Called from initialize_vtbl_ptrs via dfs_walk.  BINFO is the base
     301             :    which we want to initialize the vtable pointer for, DATA is
     302             :    TREE_LIST whose TREE_VALUE is the this ptr expression.  */
     303             : 
     304             : static tree
     305         552 : cp_ubsan_dfs_initialize_vtbl_ptrs (tree binfo, void *data)
     306             : {
     307         552 :   if (!TYPE_CONTAINS_VPTR_P (BINFO_TYPE (binfo)))
     308             :     return dfs_skip_bases;
     309             : 
     310         251 :   if (!BINFO_PRIMARY_P (binfo))
     311             :     {
     312         144 :       tree base_ptr = TREE_VALUE ((tree) data);
     313             : 
     314         144 :       base_ptr = build_base_path (PLUS_EXPR, base_ptr, binfo, /*nonnull=*/1,
     315             :                                   tf_warning_or_error);
     316             : 
     317             :       /* Compute the location of the vtpr.  */
     318         144 :       tree vtbl_ptr
     319         144 :         = build_vfield_ref (cp_build_fold_indirect_ref (base_ptr),
     320         144 :                             TREE_TYPE (binfo));
     321         144 :       gcc_assert (vtbl_ptr != error_mark_node);
     322             : 
     323             :       /* Assign NULL to the vptr.  */
     324         144 :       tree vtbl = build_zero_cst (TREE_TYPE (vtbl_ptr));
     325         144 :       tree stmt = cp_build_modify_expr (input_location, vtbl_ptr, NOP_EXPR,
     326             :                                         vtbl, tf_warning_or_error);
     327         144 :       if (vptr_via_virtual_p (binfo))
     328             :         /* If this vptr comes from a virtual base of the complete object, only
     329             :            clear it if we're in charge of virtual bases.  */
     330          16 :         stmt = build_if_in_charge (stmt);
     331         144 :       finish_expr_stmt (stmt);
     332             :     }
     333             : 
     334             :   return NULL_TREE;
     335             : }
     336             : 
     337             : /* Initialize all the vtable pointers in the object pointed to by
     338             :    ADDR to NULL, so that we catch invalid calls to methods before
     339             :    mem-initializers are completed.  */
     340             : 
     341             : void
     342         432 : cp_ubsan_maybe_initialize_vtbl_ptrs (tree addr)
     343             : {
     344         432 :   if (!cp_ubsan_instrument_vptr_p (NULL_TREE))
     345             :     return;
     346             : 
     347         432 :   tree type = TREE_TYPE (TREE_TYPE (addr));
     348         432 :   tree list = build_tree_list (type, addr);
     349             :   /* We cannot rely on the vtable being set up.  We have to indirect via the
     350             :      vtt_parm.  */
     351         432 :   int save_in_base_initializer = in_base_initializer;
     352         432 :   in_base_initializer = 1;
     353             : 
     354             :   /* Walk through the hierarchy, initializing the vptr in each base
     355             :      class to NULL.  */
     356         432 :   dfs_walk_once (TYPE_BINFO (type), cp_ubsan_dfs_initialize_vtbl_ptrs,
     357             :                  NULL, list);
     358             : 
     359         432 :   in_base_initializer = save_in_base_initializer;
     360             : }

Generated by: LCOV version 1.16