LCOV - code coverage report
Current view: top level - gcc/cp - cp-ubsan.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 99.3 % 145 144
Test Date: 2026-07-11 15:47:05 Functions: 100.0 % 11 11
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.