LCOV - code coverage report
Current view: top level - gcc/analyzer - kf-lang-cp.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 98.1 % 267 262
Test Date: 2026-08-22 16:33:35 Functions: 100.0 % 28 28
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Handling for the known behavior of various functions specific to C++.
       2              :    Copyright (C) 2020-2026 Free Software Foundation, Inc.
       3              :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       4              : 
       5              : This file is part of GCC.
       6              : 
       7              : GCC is free software; you can redistribute it and/or modify it
       8              : under the terms of the GNU General Public License as published by
       9              : the Free Software Foundation; either version 3, or (at your option)
      10              : any later version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but
      13              : WITHOUT ANY WARRANTY; without even the implied warranty of
      14              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15              : General Public License 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 "analyzer/common.h"
      22              : #include "cgraph.h"
      23              : #include "ipa-utils.h"
      24              : 
      25              : #include "diagnostic.h"
      26              : 
      27              : #include "analyzer/analyzer-logging.h"
      28              : #include "analyzer/region-model.h"
      29              : #include "analyzer/call-details.h"
      30              : 
      31              : #if ENABLE_ANALYZER
      32              : 
      33              : /* Return true if CALL is a non-allocating operator new or operator new []
      34              :    that contains no user-defined args, i.e. having any signature of:
      35              : 
      36              :     - void* operator new (std::size_t count, void* ptr);
      37              :     - void* operator new[] (std::size_t count, void* ptr);
      38              : 
      39              :    See https://en.cppreference.com/w/cpp/memory/new/operator_new.  */
      40              : 
      41        42981 : bool is_placement_new_p (const gcall &call)
      42              : {
      43        42981 :   tree fndecl = gimple_call_fndecl (&call);
      44              : 
      45        42981 :   if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
      46              :     /* Give up on overloaded operator new.  */
      47              :     return false;
      48              : 
      49        42603 :   if (!is_named_call_p (fndecl, "operator new", call, 2)
      50        42603 :       && !is_named_call_p (fndecl, "operator new []", call, 2))
      51              :     return false;
      52              : 
      53              :   /* We must distinguish between an allocating non-throwing new
      54              :     and a non-allocating new.
      55              : 
      56              :     The former might have one of the following signatures :
      57              :     void* operator new (std::size_t count, const std::nothrow_t& tag);
      58              :     void* operator new[] (std::size_t count, const std::nothrow_t& tag);
      59              :     Whereas a placement new would take a pointer.  */
      60         1155 :   tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
      61         1155 :   return TREE_CODE (TREE_VALUE (arg1_type)) == POINTER_TYPE;
      62              : }
      63              : 
      64              : namespace ana {
      65              : 
      66              : /* Implementations of specific functions.  */
      67              : 
      68              : /* Handler for __dynamic_cast.  */
      69              : 
      70              : /* A candidate TYPE subobject found on one inheritance path.  */
      71              : 
      72              : struct dyncast_subobject
      73              : {
      74           96 :   dyncast_subobject () : binfo (NULL_TREE), accessible (false) {}
      75          237 :   dyncast_subobject (tree binfo, bool accessible)
      76          237 :     : binfo (binfo), accessible (accessible)
      77              :   {}
      78              : 
      79              :   tree binfo;      /* the BINFO that represents our subobject.
      80              :                       NULL_TREE if failed.  */
      81              :   bool accessible; /* Every edge from the root was public.  */
      82              : };
      83              : 
      84              : /* Recover the class type from a type_info argument of __dynamic_cast, expected
      85              :    to be &_ZTIxxx.  The C++ FE sets TREE_TYPE on the tinfo decl's DECL_NAME
      86              :    identifier.  __dynamic_cast is callable directly, so a runtime tinfo pointer
      87              :    (an SSA name) can exist.  Return NULL_TREE on any shape mismatch.  */
      88              : 
      89              : static tree
      90          198 : get_type_from_tinfo_arg (tree arg)
      91              : {
      92          198 :   if (!arg || TREE_CODE (arg) != ADDR_EXPR)
      93              :     return NULL_TREE;
      94          198 :   tree tinfo_decl = TREE_OPERAND (arg, 0);
      95          198 :   if (!DECL_P (tinfo_decl) || !DECL_NAME (tinfo_decl))
      96              :     return NULL_TREE;
      97          198 :   tree type = TREE_TYPE (DECL_NAME (tinfo_decl));
      98          198 :   if (!type || !RECORD_OR_UNION_TYPE_P (type))
      99              :     return NULL_TREE;
     100          198 :   return TYPE_MAIN_VARIANT (type);
     101              : }
     102              : 
     103              : /* Find the sub-BINFO of BINFO that has type TARGET_TYPE and sits at the same
     104              :    address as BINFO itself (i.e. at relative offset 0) by descending through
     105              :    every base at that same (absolute) offset.  */
     106              : 
     107              : static tree
     108          216 : lookup_binfo_at_same_offset (tree binfo, tree target_type)
     109              : {
     110          216 :   if (types_same_for_odr (BINFO_TYPE (binfo), target_type))
     111              :     return binfo;
     112              : 
     113          120 :   tree offset = BINFO_OFFSET (binfo);
     114          120 :   tree base_binfo;
     115          120 :   for (unsigned i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
     116          120 :     if (tree_int_cst_equal (BINFO_OFFSET (base_binfo), offset))
     117          120 :       if (tree found = lookup_binfo_at_same_offset (base_binfo, target_type))
     118              :         return found;
     119              :   return NULL_TREE;
     120              : }
     121              : 
     122              : /* Look recursively for a BINFO that matches our DST_TYPE.  This method might
     123              :    find multiple matches. If none is found, matches won't be changed.
     124              : 
     125              :    Even if one path is private, it is still ambiguous according to the
     126              :    definition, so that case still counts as having multiple matches.  That
     127              :    means we ignore access specifiers when searching bases.
     128              : 
     129              :    Morally virtual matches of the same type under different virtual ancestors
     130              :    are distinct subobjects, i.e:
     131              : 
     132              :                          class B0 {};
     133              :         class V1 : B0 {};            class V2 : B0 {};
     134              :     class B2 : virtual V1 {};    class B4 : virtual V2 {};
     135              :                      class MD : B2, B3 {};
     136              : 
     137              :    Here, each B0 is a different subobject, so we must account for this case
     138              :    when checking virtual inheritance.  We compare the BINFO_OFFSET of all the
     139              :    BINFOs that match (the offset is relative to our most-derived object) to
     140              :    decide if they belong to the same suboject.  Note that if it is at the same
     141              :    BINFO_OFFSET and has the same TREE_TYPE, it must necessarily be the same
     142              :    subobject.  */
     143              : 
     144              : static void
     145          678 : lookup_subobject_matches (const_tree target_type, const dyncast_subobject match,
     146              :                           auto_vec<dyncast_subobject> &matches)
     147              : {
     148          678 :   if (types_same_for_odr (BINFO_TYPE (match.binfo), target_type))
     149              :     {
     150              :       /* Check if we had already found this particular subobject.  */
     151          198 :       tree match_offset = BINFO_OFFSET (match.binfo);
     152          273 :       for (auto &subobject : matches)
     153           27 :         if (tree_int_cst_equal (BINFO_OFFSET (subobject.binfo), match_offset))
     154              :           {
     155            6 :             subobject.accessible |= match.accessible;
     156            6 :             return; /* We are adding the same subobject, so skip it.  */
     157              :           }
     158          192 :       matches.safe_push (match);
     159          192 :       return;
     160              :     }
     161              :   tree parent_binfo = match.binfo;
     162              :   tree base_binfo;
     163          936 :   for (unsigned i = 0; BINFO_BASE_ITERATE (parent_binfo, i, base_binfo); i++)
     164              :     {
     165          456 :       dyncast_subobject child = match;
     166          456 :       child.binfo = base_binfo;
     167              :       /* If BINFO_BASE_ACCESSES is not present, public access is implied.  */
     168          912 :       child.accessible
     169          912 :         &= !BINFO_BASE_ACCESSES (parent_binfo)
     170          456 :            || BINFO_BASE_ACCESS (parent_binfo, i) == access_public_node;
     171              :       /* Check if the next binfo might be our DST_TYPE binfo recursively.  */
     172          456 :       lookup_subobject_matches (target_type, child, matches);
     173              :     }
     174              : }
     175              : 
     176              : /* We implement the runtime check rules as per [expr.dynamic.cast]9.
     177              :    As a general overview, those rules state:
     178              :      [expr.dynamic.cast]/9.1: Does SRC_OBJ point to a public base subobject of
     179              :        a DST_TYPE object?  And is there only one DST_TYPE object derived from
     180              :        SRC_OBJ?
     181              :        We expect the hierarchy to be something like:
     182              :        SRC_TYPE -> ... -> DST_TYPE -> ... -> MD_TYPE.
     183              : 
     184              :      [expr.dynamic.cast]/9.2: Otherwise, does SRC_OBJ point to a public base
     185              :        subobject of a MDTYPE object?  And is DST_TYPE an unambiguous and public
     186              :        base of MDTYPE?
     187              :        We expect the hierarchy to be something like:
     188              :        MD_TYPE -> ... -> DST_TYPE
     189              : 
     190              :      [expr.dynamic.cast]/9.3: Otherwise, the runtime check fails.  */
     191              : 
     192              : static dyncast_subobject
     193           96 : evaluate_dyncast (tree dst_type, tree md_binfo, tree src_binfo)
     194              : {
     195           96 :   dyncast_subobject no_base_match;
     196              : 
     197              :   /* Start by assuming the path will be public.  */
     198           96 :   auto_vec<dyncast_subobject> dst_matches;
     199           96 :   dyncast_subobject md_subobject = {md_binfo, /* accessible = */ true};
     200           96 :   lookup_subobject_matches (dst_type, md_subobject, dst_matches);
     201              : 
     202              :   /* Per [expr.dynamic.cast]/9.1:
     203              :      Only one object of DST_TYPE can be derived from this SRC_OBJ and
     204              :      The path from DST -> SRC must be public.  */
     205           96 :   tree src_offset = BINFO_OFFSET (src_binfo);
     206           96 :   tree src_type = BINFO_TYPE (src_binfo);
     207              : 
     208           96 :   auto_vec<dyncast_subobject> clause1_matches;
     209          351 :   for (const auto &dst_subobj : dst_matches)
     210              :     {
     211           93 :       auto_vec<dyncast_subobject> src_matches;
     212           93 :       dyncast_subobject from_dst = {dst_subobj.binfo, /* accessible = */ true};
     213           93 :       lookup_subobject_matches (src_type, from_dst, src_matches);
     214              :       /* Only keep matches that derive from this src subobject.  */
     215          267 :       for (const auto &src_subobj : src_matches)
     216           60 :         if (tree_int_cst_equal (BINFO_OFFSET (src_subobj.binfo), src_offset))
     217              :           /* Keep dst BINFO but save whether SRC is a public base of DST.  */
     218           48 :           clause1_matches.safe_push ({dst_subobj.binfo, src_subobj.accessible});
     219           93 :     }
     220          138 :   if (clause1_matches.length () == 1 && clause1_matches[0].accessible)
     221           30 :     return clause1_matches[0]; /* No ambiguity, only one public match.  */
     222              : 
     223              :   /* No match or the match we found was private.  Try clause 2.  */
     224              : 
     225              :   /* Otherwise, per [expr.dynamic.cast]/9.2:
     226              :       Require a public path from MD_OBJ -> SRC_OBJ and
     227              :       Require that DST_TYPE is an unambiguous and public base of MD_TYPE.  */
     228          105 :   if (dst_matches.length () == 1 && dst_matches[0].accessible)
     229              :     {
     230           33 :       auto_vec<dyncast_subobject> src_matches;
     231           33 :       lookup_subobject_matches (src_type, md_subobject, src_matches);
     232              :       /* Find any public path from MD_OBJ -> SRC_OBJ.  */
     233          117 :       for (const auto &src_subobj : src_matches)
     234           33 :         if (src_subobj.accessible)
     235           15 :           return dst_matches[0]; /* Found a public match.  */
     236           33 :     }
     237           51 :   return no_base_match; /* No match or the match we found was private.  */
     238           96 : }
     239              : 
     240         3501 : class kf_dynamic_cast : public known_function
     241              : {
     242              : public:
     243          663 :   bool matches_call_types_p (const call_details &cd) const final override
     244              :   {
     245              :     /* A call will look something like:
     246              :        Derived *d;
     247              :        d = __dynamic_cast ((Base*) b, &_ZTI1Base, &_ZTI1Derived, 8);  */
     248         1326 :     return (cd.num_args () == 4 && POINTER_TYPE_P (cd.get_arg_type (0))
     249          663 :             && POINTER_TYPE_P (cd.get_arg_type (1))
     250          663 :             && POINTER_TYPE_P (cd.get_arg_type (2))
     251         1326 :             && INTEGRAL_TYPE_P (cd.get_arg_type (3)));
     252              :   }
     253           99 :   void impl_call_post (const call_details &cd) const final override
     254              :   {
     255           99 :     region_model *model = cd.get_model ();
     256           99 :     region_model_manager *mgr = cd.get_manager ();
     257              : 
     258           99 :     cd.set_any_lhs_with_defaults ();
     259              : 
     260           99 :     tree dst_ptr_type = cd.get_lhs_type ();
     261           99 :     if (!dst_ptr_type)
     262           54 :       return;
     263              : 
     264              :     /* Recover the class types from the tinfo args.  */
     265           99 :     tree src_type = get_type_from_tinfo_arg (cd.get_arg_tree (1));
     266           99 :     tree dst_type = get_type_from_tinfo_arg (cd.get_arg_tree (2));
     267           99 :     if (!src_type || !dst_type)
     268              :       return;
     269              : 
     270              :     /* Read the vptr binding of the object; VPTR_OFF selects the
     271              :        sub-vtable within the vtable decl, so it identifies which subobject's
     272              :        vptr we read.  */
     273           99 :     tree src_obj = cd.get_arg_tree (0);
     274           99 :     unsigned HOST_WIDE_INT vptr_off;
     275           99 :     tree vtable
     276           99 :       = model->get_vtable_from_obj (src_obj, src_type, mgr, nullptr, &vptr_off);
     277              :     /* The class the vtable belongs to is the dynamic (most-derived) type of
     278              :        the object.  VTABLE is whatever decl the vptr slot happened to point at,
     279              :        so check it really is a vtable.  */
     280           99 :     if (!vtable || !VAR_P (vtable) || !DECL_VIRTUAL_P (vtable))
     281              :       return;
     282           96 :     tree mdtype = DECL_CONTEXT (vtable);
     283           96 :     if (!mdtype || !RECORD_OR_UNION_TYPE_P (mdtype))
     284              :       return;
     285           96 :     tree md_binfo = TYPE_BINFO (mdtype);
     286           96 :     if (!md_binfo)
     287              :       return;
     288              : 
     289              :     /* Given the value stored to SRC_OBJ's vtpr field (&_ZTV* + offset), find
     290              :        which subobject of this hierarchy would have this value written into its
     291              :        vptr.  */
     292           96 :     tree vtable_binfo
     293           96 :       = subbinfo_with_vtable_at_offset (md_binfo, vptr_off, vtable);
     294           96 :     if (!vtable_binfo)
     295              :       return;
     296              :     /* With a shared primary-base vtable the owning binfo may be an enclosing
     297              :        type.  Consider:
     298              :          class A {};
     299              :          class B {};
     300              :          class C : B {};
     301              :          class D : A, C {};
     302              :        Here the vptr value stored in the B-subobject's slot is owned by the C
     303              :        binfo (C's sub-vtable group), and a lookup with B's vptr value returns
     304              :        the C binfo, not B's (BINFO_VTABLE is only set on the owner, cf.
     305              :        ipa-devirt.cc:61).  In this case, the src subobject sits on its primary
     306              :        chain (relative offset 0), which lookup_binfo_at_same_offset finds.  */
     307           96 :     tree src_binfo = lookup_binfo_at_same_offset (vtable_binfo, src_type);
     308           96 :     if (!src_binfo)
     309              :       return;
     310              : 
     311           96 :     dyncast_subobject dst_match
     312           96 :       = evaluate_dyncast (dst_type, md_binfo, src_binfo);
     313              : 
     314           96 :     if (!dst_match.binfo)
     315              :       { /* [expr.dynamic.cast]/9.3: Otherwise, the runtime check failed.  */
     316           51 :         cd.maybe_set_lhs (mgr->get_or_create_null_ptr (dst_ptr_type));
     317           51 :         return;
     318              :       }
     319              : 
     320              :     /* Build a pointer to the dst subobject.  Work in byte offsets relative to
     321              :        SRC_REG's base region; we never need a region for the mdtype object
     322              :        itself, only its start offset, recovered from where the src subobject
     323              :        sits within MDTYPE.  */
     324           45 :     const region *src_reg = cd.deref_ptr_arg (0);
     325           45 :     region_offset off = src_reg->get_offset (mgr);
     326           45 :     if (!off.concrete_p ())
     327              :       return; /* Bail, leave lhs conjured.  */
     328           45 :     byte_offset_t src_obj_start;
     329           45 :     if (!off.get_concrete_byte_offset (&src_obj_start))
     330              :       return;
     331              : 
     332           45 :     HOST_WIDE_INT src_off_in_md = tree_to_shwi (BINFO_OFFSET (src_binfo));
     333           45 :     HOST_WIDE_INT dst_off_in_md = tree_to_shwi (BINFO_OFFSET (dst_match.binfo));
     334           45 :     HOST_WIDE_INT md_start_in_base = src_obj_start.to_shwi () - src_off_in_md;
     335           45 :     if (md_start_in_base < 0)
     336              :       return; /* Layout disagreement between the store and the binfo data;
     337              :                  bail rather than build a negative-offset region.  */
     338           45 :     HOST_WIDE_INT dst_off_in_base = md_start_in_base + dst_off_in_md;
     339              : 
     340              :     /* BASE_REG is the outermost region, not necessarily the mdtype
     341              :        object (it might sit at a nonzero offset inside BASE_REG, e.g. as an
     342              :        array element or a member subobject).  The store binds values by byte
     343              :        ranges within a base region, so a concrete offset_region aliases the
     344              :        FE's field-path accesses to the same bytes.  */
     345           45 :     const region *base_reg = off.get_base_region ();
     346           45 :     const svalue *dst_off_sval
     347           45 :       = mgr->get_or_create_int_cst (size_type_node, dst_off_in_base);
     348           45 :     const region *dst_reg
     349           45 :       = mgr->get_offset_region (base_reg, dst_type, dst_off_sval);
     350           45 :     cd.maybe_set_lhs (mgr->get_ptr_svalue (dst_ptr_type, dst_reg));
     351              :   }
     352              : };
     353              : 
     354              : /* Handler for "operator new" and "operator new []".  */
     355              : 
     356         7002 : class kf_operator_new : public known_function
     357              : {
     358              : public:
     359         4437 :   bool matches_call_types_p (const call_details &cd) const final override
     360              :   {
     361         4437 :     return (cd.num_args () == 1
     362         2130 :       && cd.arg_is_size_p (0))
     363         4437 :       || (cd.num_args () == 2
     364         2307 :       && cd.arg_is_size_p (0)
     365         2307 :       && POINTER_TYPE_P (cd.get_arg_type (1)));
     366              :   }
     367              : 
     368              :   void
     369          309 :   check_any_preconditions (const call_details &cd) const final override
     370              :   {
     371          309 :     region_model_context *ctxt = cd.get_ctxt ();
     372          309 :     if (!ctxt)
     373              :       return;
     374          309 :     region_model *model = cd.get_model ();
     375          309 :     const gcall &call = cd.get_call_stmt ();
     376              : 
     377              :     /* If the call was actually a placement new, check that accessing
     378              :        the buffer lhs is placed into does not result in out-of-bounds.  */
     379          309 :     if (is_placement_new_p (call))
     380              :       {
     381           69 :         if (const region *sized_reg = get_sized_region_for_placement_new (cd))
     382           57 :           model->check_region_for_write (sized_reg,
     383              :                                          nullptr,
     384              :                                          ctxt);
     385              :       }
     386              :   }
     387              : 
     388         1578 :   void impl_call_pre (const call_details &cd) const final override
     389              :   {
     390         1578 :     region_model *model = cd.get_model ();
     391         1578 :     region_model_manager *mgr = cd.get_manager ();
     392         1578 :     const svalue *size_sval = cd.get_arg_svalue (0);
     393         1578 :     region_model_context *ctxt = cd.get_ctxt ();
     394         1578 :     const gcall &call = cd.get_call_stmt ();
     395              : 
     396         1578 :     if (is_placement_new_p (call))
     397              :       {
     398           12 :         const region *ptr_reg = cd.deref_ptr_arg (1);
     399           12 :         if (ptr_reg && cd.get_lhs_type ())
     400            0 :           if (const region *sized_reg = get_sized_region_for_placement_new (cd))
     401              :             {
     402            0 :               const svalue *ptr_sval
     403            0 :                 = mgr->get_ptr_svalue (cd.get_lhs_type (), sized_reg);
     404            0 :               cd.maybe_set_lhs (ptr_sval);
     405              :             }
     406              :       }
     407              :     /* If the call is an allocating new, then create a heap allocated
     408              :        region.  */
     409              :     else
     410              :       {
     411         1566 :         const region *new_reg
     412         1566 :           = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
     413         1566 :         if (cd.get_lhs_type ())
     414              :           {
     415         1566 :             const svalue *ptr_sval
     416         1566 :               = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     417         1566 :             cd.maybe_set_lhs (ptr_sval);
     418              :           }
     419              :       }
     420         1578 :   }
     421              : 
     422         1578 :   void impl_call_post (const call_details &cd) const final override
     423              :   {
     424         1578 :     region_model *model = cd.get_model ();
     425         1578 :     region_model_manager *mgr = cd.get_manager ();
     426         1578 :     tree callee_fndecl = cd.get_fndecl_for_call ();
     427         1578 :     region_model_context *ctxt = cd.get_ctxt ();
     428              : 
     429              :     /* If the call is guaranteed to return nonnull
     430              :        then add a nonnull constraint to the allocated region.  */
     431         1578 :     if (!TREE_NOTHROW (callee_fndecl)
     432          600 :         && flag_exceptions
     433         2034 :         && cd.get_lhs_type ())
     434              :       {
     435          444 :         const svalue *null_sval
     436          444 :           = mgr->get_or_create_null_ptr (cd.get_lhs_type ());
     437          444 :         const svalue *result
     438          444 :           = model->get_store_value (cd.get_lhs_region (), ctxt);
     439          444 :         model->add_constraint (result, NE_EXPR, null_sval, ctxt);
     440              :       }
     441         1578 :   }
     442              : 
     443              : private:
     444              :   const region *
     445           69 :   get_sized_region_for_placement_new (const call_details &cd) const
     446              :   {
     447           69 :     const region *ptr_reg = cd.deref_ptr_arg (1);
     448           69 :     if (ptr_reg && cd.get_lhs_type ())
     449              :       {
     450           57 :         region_model_manager *mgr = cd.get_manager ();
     451           57 :         const svalue *num_bytes_sval = cd.get_arg_svalue (0);
     452           57 :         return mgr->get_sized_region (ptr_reg,
     453              :                                       cd.get_lhs_type (),
     454           57 :                                       num_bytes_sval);
     455              :       }
     456              :     return nullptr;
     457              :   }
     458              : };
     459              : 
     460              : /* Handler for "operator delete" and for "operator delete []",
     461              :    both the sized and unsized variants
     462              :    (2 arguments and 1 argument respectively).  */
     463              : 
     464         7002 : class kf_operator_delete : public known_function
     465              : {
     466              : public:
     467         2574 :   bool matches_call_types_p (const call_details &cd) const final override
     468              :   {
     469         2574 :     return cd.num_args () == 1 or cd.num_args () == 2;
     470              :   }
     471              : 
     472          494 :   void impl_call_post (const call_details &cd) const final override
     473              :   {
     474          494 :     region_model *model = cd.get_model ();
     475          494 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     476          494 :     if (const region *freed_reg = ptr_sval->maybe_get_region ())
     477              :       {
     478              :         /* If the ptr points to an underlying heap region, delete it,
     479              :            poisoning pointers.  */
     480          274 :         model->unbind_region_and_descendents (freed_reg,
     481              :                                               poison_kind::deleted);
     482              :       }
     483          494 :   }
     484              : 
     485              : };
     486              : 
     487         3501 : class kf_cxa_allocate_exception : public known_function
     488              : {
     489              : public:
     490         1325 :   bool matches_call_types_p (const call_details &cd) const final override
     491              :   {
     492         1325 :     return cd.num_args () == 1 && cd.arg_is_size_p (0);
     493              :   }
     494              : 
     495          305 :   void impl_call_pre (const call_details &cd) const final override
     496              :   {
     497          305 :     region_model *model = cd.get_model ();
     498          305 :     region_model_manager *mgr = cd.get_manager ();
     499          305 :     const svalue *size_sval = cd.get_arg_svalue (0);
     500          305 :     region_model_context *ctxt = cd.get_ctxt ();
     501              : 
     502              :     /* Create a heap allocated region.  */
     503          305 :     const region *new_reg
     504          305 :       = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
     505          305 :     if (cd.get_lhs_type ())
     506              :       {
     507          305 :         const svalue *ptr_sval
     508          305 :           = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     509          305 :         cd.maybe_set_lhs (ptr_sval);
     510              :       }
     511          305 :   }
     512              : };
     513              : 
     514         3501 : class kf_cxa_begin_catch : public known_function
     515              : {
     516              : public:
     517         1404 :   bool matches_call_types_p (const call_details &cd) const final override
     518              :   {
     519         1404 :     return (cd.num_args () == 1
     520         1404 :             && POINTER_TYPE_P (cd.get_arg_type (0)));
     521              :   }
     522              : 
     523          337 :   void impl_call_pre (const call_details &cd) const final override
     524              :   {
     525          337 :     region_model *model = cd.get_model ();
     526              : 
     527          337 :     auto node = model->pop_thrown_exception ();
     528          337 :     model->push_caught_exception (node);
     529          337 :     cd.maybe_set_lhs (node.m_exception_sval);
     530          337 :   }
     531              : };
     532              : 
     533         3501 : class kf_cxa_end_catch : public known_function
     534              : {
     535              : public:
     536         1292 :   bool matches_call_types_p (const call_details &cd) const final override
     537              :   {
     538         1292 :     return cd.num_args () == 0;
     539              :   }
     540              : 
     541          221 :   void impl_call_pre (const call_details &cd) const final override
     542              :   {
     543          221 :     region_model *model = cd.get_model ();
     544          221 :     model->pop_caught_exception ();
     545          221 :   }
     546              : };
     547              : 
     548              : /* A subclass of pending_diagnostic for complaining about an exception
     549              :    of an unexpected type being thrown (due to a call to
     550              :    __cxa_call_unexpected).
     551              :    See https://en.cppreference.com/w/cpp/language/except_spec  */
     552              : 
     553              : class throw_of_unexpected_type
     554              : : public pending_diagnostic_subclass<throw_of_unexpected_type>
     555              : {
     556              : public:
     557            2 :   throw_of_unexpected_type (tree exception_type,
     558              :                             tree thrown_from_fndecl)
     559            2 :   : m_exception_type (exception_type),
     560            2 :     m_thrown_from_fndecl (thrown_from_fndecl)
     561              :   {
     562            2 :     gcc_assert (m_exception_type);
     563            2 :     gcc_assert (m_thrown_from_fndecl);
     564            2 :   }
     565              : 
     566           13 :   const char *get_kind () const final override
     567              :   {
     568           13 :     return "throw_of_unexpected_type";
     569              :   }
     570              : 
     571            2 :   bool operator== (const throw_of_unexpected_type &other) const
     572              :   {
     573            2 :     return (m_exception_type == other.m_exception_type
     574            2 :             && m_thrown_from_fndecl == other.m_thrown_from_fndecl);
     575              :   }
     576              : 
     577            4 :   int get_controlling_option () const final override
     578              :   {
     579            4 :     return OPT_Wanalyzer_throw_of_unexpected_type;
     580              :   }
     581              : 
     582            2 :   bool emit (diagnostic_emission_context &ctxt) final override
     583              :   {
     584            2 :     auto_diagnostic_group d;
     585              : 
     586            2 :     bool warned
     587            2 :       = ctxt.warn ("throwing exception of unexpected type %qT from %qE",
     588              :                    m_exception_type, m_thrown_from_fndecl);
     589            2 :     if (warned)
     590              :       {
     591            2 :         inform (DECL_SOURCE_LOCATION (m_thrown_from_fndecl),
     592              :                 "%qE declared here", m_thrown_from_fndecl);
     593              :         // TODO: show specified types?
     594              :       }
     595            4 :     return warned;
     596            2 :   }
     597              : 
     598              :   bool
     599            4 :   describe_final_event (pretty_printer &pp,
     600              :                         const evdesc::final_event &) final override
     601              :   {
     602            4 :     pp_printf  (&pp,
     603              :                 "exception of unexpected type %qT thrown from %qE",
     604              :                 m_exception_type, m_thrown_from_fndecl);
     605            4 :     return true;
     606              :   }
     607              : 
     608              : private:
     609              :   tree m_exception_type;
     610              :   tree m_thrown_from_fndecl;
     611              : };
     612              : 
     613              : /* See https://en.cppreference.com/w/cpp/language/except_spec  */
     614              : 
     615         3501 : class kf_cxa_call_unexpected : public known_function
     616              : {
     617              : public:
     618           12 :   bool matches_call_types_p (const call_details &cd) const final override
     619              :   {
     620           12 :     return (cd.num_args () == 1
     621           12 :             && POINTER_TYPE_P (cd.get_arg_type (0)));
     622              :   }
     623              : 
     624            2 :   void impl_call_pre (const call_details &cd) const final override
     625              :   {
     626            2 :     if (region_model_context *ctxt = cd.get_ctxt ())
     627              :       {
     628            2 :         region_model *model = cd.get_model ();
     629            2 :         tree thrown_from_fndecl = model->get_current_function ()->decl;
     630              :         /* We must have a thrown exception.  */
     631            2 :         auto eh_node = model->get_current_thrown_exception ();
     632            0 :         gcc_assert (eh_node);
     633            2 :         tree exception_type = eh_node->maybe_get_type ();
     634            2 :         ctxt->warn
     635            2 :           (std::make_unique<throw_of_unexpected_type> (exception_type,
     636              :                                                        thrown_from_fndecl));
     637            2 :         ctxt->terminate_path ();
     638              :       }
     639            2 :   }
     640              : };
     641              : 
     642              : /* Populate KFM with instances of known functions relating to C++.  */
     643              : 
     644              : void
     645         3501 : register_known_functions_lang_cp (known_function_manager &kfm)
     646              : {
     647         3501 :   kfm.add ("operator new", std::make_unique<kf_operator_new> ());
     648         3501 :   kfm.add ("operator new []", std::make_unique<kf_operator_new> ());
     649         3501 :   kfm.add ("operator delete", std::make_unique<kf_operator_delete> ());
     650         3501 :   kfm.add ("operator delete []", std::make_unique<kf_operator_delete> ());
     651              : 
     652              :   /* Functions mentioned in "Itanium C++ ABI: Exception Handling"'s
     653              :      "Level II: C++ ABI"
     654              :      https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-abi  */
     655         3501 :   kfm.add ("__cxa_allocate_exception",
     656         3501 :            std::make_unique<kf_cxa_allocate_exception> ());
     657              :   // We treat __cxa_throw and __cxa_rethrow as special cases
     658         3501 :   kfm.add ("__cxa_begin_catch", std::make_unique<kf_cxa_begin_catch> ());
     659         3501 :   kfm.add ("__cxa_end_catch", std::make_unique<kf_cxa_end_catch> ());
     660         3501 :   kfm.add ("__cxa_call_unexpected",
     661         3501 :            std::make_unique<kf_cxa_call_unexpected> ());
     662              : 
     663              :   /* Itanium C++ ABI's "The dynamic_cast Algorithm"
     664              :      https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dynamic_cast-algorithm
     665              :    */
     666         3501 :   kfm.add ("__dynamic_cast", std::make_unique<kf_dynamic_cast> ());
     667         3501 : }
     668              : 
     669              : } // namespace ana
     670              : 
     671              : #endif /* #if ENABLE_ANALYZER */
        

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.