LCOV - code coverage report
Current view: top level - gcc/analyzer - kf-lang-cp.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 96.8 % 155 150
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 22 22
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              : 
      23              : #include "diagnostic.h"
      24              : 
      25              : #include "analyzer/analyzer-logging.h"
      26              : #include "analyzer/region-model.h"
      27              : #include "analyzer/call-details.h"
      28              : 
      29              : #if ENABLE_ANALYZER
      30              : 
      31              : /* Return true if CALL is a non-allocating operator new or operator new []
      32              :    that contains no user-defined args, i.e. having any signature of:
      33              : 
      34              :     - void* operator new (std::size_t count, void* ptr);
      35              :     - void* operator new[] (std::size_t count, void* ptr);
      36              : 
      37              :    See https://en.cppreference.com/w/cpp/memory/new/operator_new.  */
      38              : 
      39        40880 : bool is_placement_new_p (const gcall &call)
      40              : {
      41        40880 :   tree fndecl = gimple_call_fndecl (&call);
      42              : 
      43        40880 :   if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
      44              :     /* Give up on overloaded operator new.  */
      45              :     return false;
      46              : 
      47        40524 :   if (!is_named_call_p (fndecl, "operator new", call, 2)
      48        40524 :       && !is_named_call_p (fndecl, "operator new []", call, 2))
      49              :     return false;
      50              : 
      51              :   /* We must distinguish between an allocating non-throwing new
      52              :     and a non-allocating new.
      53              : 
      54              :     The former might have one of the following signatures :
      55              :     void* operator new (std::size_t count, const std::nothrow_t& tag);
      56              :     void* operator new[] (std::size_t count, const std::nothrow_t& tag);
      57              :     Whereas a placement new would take a pointer.  */
      58         1065 :   tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
      59         1065 :   return TREE_CODE (TREE_VALUE (arg1_type)) == POINTER_TYPE;
      60              : }
      61              : 
      62              : namespace ana {
      63              : 
      64              : /* Implementations of specific functions.  */
      65              : 
      66              : /* Handler for "operator new" and "operator new []".  */
      67              : 
      68         6754 : class kf_operator_new : public known_function
      69              : {
      70              : public:
      71         3348 :   bool matches_call_types_p (const call_details &cd) const final override
      72              :   {
      73         3348 :     return (cd.num_args () == 1
      74         1221 :       && cd.arg_is_size_p (0))
      75         3348 :       || (cd.num_args () == 2
      76         2127 :       && cd.arg_is_size_p (0)
      77         2127 :       && POINTER_TYPE_P (cd.get_arg_type (1)));
      78              :   }
      79              : 
      80              :   void
      81          228 :   check_any_preconditions (const call_details &cd) const final override
      82              :   {
      83          228 :     region_model_context *ctxt = cd.get_ctxt ();
      84          228 :     if (!ctxt)
      85              :       return;
      86          228 :     region_model *model = cd.get_model ();
      87          228 :     const gcall &call = cd.get_call_stmt ();
      88              : 
      89              :     /* If the call was actually a placement new, check that accessing
      90              :        the buffer lhs is placed into does not result in out-of-bounds.  */
      91          228 :     if (is_placement_new_p (call))
      92              :       {
      93           69 :         if (const region *sized_reg = get_sized_region_for_placement_new (cd))
      94           57 :           model->check_region_for_write (sized_reg,
      95              :                                          nullptr,
      96              :                                          ctxt);
      97              :       }
      98              :   }
      99              : 
     100         1236 :   void impl_call_pre (const call_details &cd) const final override
     101              :   {
     102         1236 :     region_model *model = cd.get_model ();
     103         1236 :     region_model_manager *mgr = cd.get_manager ();
     104         1236 :     const svalue *size_sval = cd.get_arg_svalue (0);
     105         1236 :     region_model_context *ctxt = cd.get_ctxt ();
     106         1236 :     const gcall &call = cd.get_call_stmt ();
     107              : 
     108         1236 :     if (is_placement_new_p (call))
     109              :       {
     110           12 :         const region *ptr_reg = cd.deref_ptr_arg (1);
     111           12 :         if (ptr_reg && cd.get_lhs_type ())
     112            0 :           if (const region *sized_reg = get_sized_region_for_placement_new (cd))
     113              :             {
     114            0 :               const svalue *ptr_sval
     115            0 :                 = mgr->get_ptr_svalue (cd.get_lhs_type (), sized_reg);
     116            0 :               cd.maybe_set_lhs (ptr_sval);
     117              :             }
     118              :       }
     119              :     /* If the call is an allocating new, then create a heap allocated
     120              :        region.  */
     121              :     else
     122              :       {
     123         1224 :         const region *new_reg
     124         1224 :           = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
     125         1224 :         if (cd.get_lhs_type ())
     126              :           {
     127         1224 :             const svalue *ptr_sval
     128         1224 :               = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     129         1224 :             cd.maybe_set_lhs (ptr_sval);
     130              :           }
     131              :       }
     132         1236 :   }
     133              : 
     134         1236 :   void impl_call_post (const call_details &cd) const final override
     135              :   {
     136         1236 :     region_model *model = cd.get_model ();
     137         1236 :     region_model_manager *mgr = cd.get_manager ();
     138         1236 :     tree callee_fndecl = cd.get_fndecl_for_call ();
     139         1236 :     region_model_context *ctxt = cd.get_ctxt ();
     140              : 
     141              :     /* If the call is guaranteed to return nonnull
     142              :        then add a nonnull constraint to the allocated region.  */
     143         1236 :     if (!TREE_NOTHROW (callee_fndecl)
     144          348 :         && flag_exceptions
     145         1494 :         && cd.get_lhs_type ())
     146              :       {
     147          246 :         const svalue *null_sval
     148          246 :           = mgr->get_or_create_null_ptr (cd.get_lhs_type ());
     149          246 :         const svalue *result
     150          246 :           = model->get_store_value (cd.get_lhs_region (), ctxt);
     151          246 :         model->add_constraint (result, NE_EXPR, null_sval, ctxt);
     152              :       }
     153         1236 :   }
     154              : 
     155              : private:
     156              :   const region *
     157           69 :   get_sized_region_for_placement_new (const call_details &cd) const
     158              :   {
     159           69 :     const region *ptr_reg = cd.deref_ptr_arg (1);
     160           69 :     if (ptr_reg && cd.get_lhs_type ())
     161              :       {
     162           57 :         region_model_manager *mgr = cd.get_manager ();
     163           57 :         const svalue *num_bytes_sval = cd.get_arg_svalue (0);
     164           57 :         return mgr->get_sized_region (ptr_reg,
     165              :                                       cd.get_lhs_type (),
     166           57 :                                       num_bytes_sval);
     167              :       }
     168              :     return nullptr;
     169              :   }
     170              : };
     171              : 
     172              : /* Handler for "operator delete" and for "operator delete []",
     173              :    both the sized and unsized variants
     174              :    (2 arguments and 1 argument respectively).  */
     175              : 
     176         6754 : class kf_operator_delete : public known_function
     177              : {
     178              : public:
     179         1338 :   bool matches_call_types_p (const call_details &cd) const final override
     180              :   {
     181         1338 :     return cd.num_args () == 1 or cd.num_args () == 2;
     182              :   }
     183              : 
     184          242 :   void impl_call_post (const call_details &cd) const final override
     185              :   {
     186          242 :     region_model *model = cd.get_model ();
     187          242 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     188          242 :     if (const region *freed_reg = ptr_sval->maybe_get_region ())
     189              :       {
     190              :         /* If the ptr points to an underlying heap region, delete it,
     191              :            poisoning pointers.  */
     192          195 :         model->unbind_region_and_descendents (freed_reg,
     193              :                                               poison_kind::deleted);
     194              :       }
     195          242 :   }
     196              : 
     197              : };
     198              : 
     199         3377 : class kf_cxa_allocate_exception : public known_function
     200              : {
     201              : public:
     202          874 :   bool matches_call_types_p (const call_details &cd) const final override
     203              :   {
     204          874 :     return cd.num_args () == 1 && cd.arg_is_size_p (0);
     205              :   }
     206              : 
     207          172 :   void impl_call_pre (const call_details &cd) const final override
     208              :   {
     209          172 :     region_model *model = cd.get_model ();
     210          172 :     region_model_manager *mgr = cd.get_manager ();
     211          172 :     const svalue *size_sval = cd.get_arg_svalue (0);
     212          172 :     region_model_context *ctxt = cd.get_ctxt ();
     213              : 
     214              :     /* Create a heap allocated region.  */
     215          172 :     const region *new_reg
     216          172 :       = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
     217          172 :     if (cd.get_lhs_type ())
     218              :       {
     219          172 :         const svalue *ptr_sval
     220          172 :           = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     221          172 :         cd.maybe_set_lhs (ptr_sval);
     222              :       }
     223          172 :   }
     224              : };
     225              : 
     226         3377 : class kf_cxa_begin_catch : public known_function
     227              : {
     228              : public:
     229          979 :   bool matches_call_types_p (const call_details &cd) const final override
     230              :   {
     231          979 :     return (cd.num_args () == 1
     232          979 :             && POINTER_TYPE_P (cd.get_arg_type (0)));
     233              :   }
     234              : 
     235          187 :   void impl_call_pre (const call_details &cd) const final override
     236              :   {
     237          187 :     region_model *model = cd.get_model ();
     238              : 
     239          187 :     auto node = model->pop_thrown_exception ();
     240          187 :     model->push_caught_exception (node);
     241          187 :     cd.maybe_set_lhs (node.m_exception_sval);
     242          187 :   }
     243              : };
     244              : 
     245         3377 : class kf_cxa_end_catch : public known_function
     246              : {
     247              : public:
     248         1057 :   bool matches_call_types_p (const call_details &cd) const final override
     249              :   {
     250         1057 :     return cd.num_args () == 0;
     251              :   }
     252              : 
     253          166 :   void impl_call_pre (const call_details &cd) const final override
     254              :   {
     255          166 :     region_model *model = cd.get_model ();
     256          166 :     model->pop_caught_exception ();
     257          166 :   }
     258              : };
     259              : 
     260              : /* A subclass of pending_diagnostic for complaining about an exception
     261              :    of an unexpected type being thrown (due to a call to
     262              :    __cxa_call_unexpected).
     263              :    See https://en.cppreference.com/w/cpp/language/except_spec  */
     264              : 
     265              : class throw_of_unexpected_type
     266              : : public pending_diagnostic_subclass<throw_of_unexpected_type>
     267              : {
     268              : public:
     269            3 :   throw_of_unexpected_type (tree exception_type,
     270              :                             tree thrown_from_fndecl)
     271            3 :   : m_exception_type (exception_type),
     272            3 :     m_thrown_from_fndecl (thrown_from_fndecl)
     273              :   {
     274            3 :     gcc_assert (m_exception_type);
     275            3 :     gcc_assert (m_thrown_from_fndecl);
     276            3 :   }
     277              : 
     278           24 :   const char *get_kind () const final override
     279              :   {
     280           24 :     return "throw_of_unexpected_type";
     281              :   }
     282              : 
     283            3 :   bool operator== (const throw_of_unexpected_type &other) const
     284              :   {
     285            3 :     return (m_exception_type == other.m_exception_type
     286            3 :             && m_thrown_from_fndecl == other.m_thrown_from_fndecl);
     287              :   }
     288              : 
     289            6 :   int get_controlling_option () const final override
     290              :   {
     291            6 :     return OPT_Wanalyzer_throw_of_unexpected_type;
     292              :   }
     293              : 
     294            3 :   bool emit (diagnostic_emission_context &ctxt) final override
     295              :   {
     296            3 :     auto_diagnostic_group d;
     297              : 
     298            3 :     bool warned
     299            3 :       = ctxt.warn ("throwing exception of unexpected type %qT from %qE",
     300              :                    m_exception_type, m_thrown_from_fndecl);
     301            3 :     if (warned)
     302              :       {
     303            3 :         inform (DECL_SOURCE_LOCATION (m_thrown_from_fndecl),
     304              :                 "%qE declared here", m_thrown_from_fndecl);
     305              :         // TODO: show specified types?
     306              :       }
     307            6 :     return warned;
     308            3 :   }
     309              : 
     310              :   bool
     311            6 :   describe_final_event (pretty_printer &pp,
     312              :                         const evdesc::final_event &) final override
     313              :   {
     314            6 :     pp_printf  (&pp,
     315              :                 "exception of unexpected type %qT thrown from %qE",
     316              :                 m_exception_type, m_thrown_from_fndecl);
     317            6 :     return true;
     318              :   }
     319              : 
     320              : private:
     321              :   tree m_exception_type;
     322              :   tree m_thrown_from_fndecl;
     323              : };
     324              : 
     325              : /* See https://en.cppreference.com/w/cpp/language/except_spec  */
     326              : 
     327         3377 : class kf_cxa_call_unexpected : public known_function
     328              : {
     329              : public:
     330           18 :   bool matches_call_types_p (const call_details &cd) const final override
     331              :   {
     332           18 :     return (cd.num_args () == 1
     333           18 :             && POINTER_TYPE_P (cd.get_arg_type (0)));
     334              :   }
     335              : 
     336            3 :   void impl_call_pre (const call_details &cd) const final override
     337              :   {
     338            3 :     if (region_model_context *ctxt = cd.get_ctxt ())
     339              :       {
     340            3 :         region_model *model = cd.get_model ();
     341            3 :         tree thrown_from_fndecl = model->get_current_function ()->decl;
     342              :         /* We must have a thrown exception.  */
     343            3 :         auto eh_node = model->get_current_thrown_exception ();
     344            0 :         gcc_assert (eh_node);
     345            3 :         tree exception_type = eh_node->maybe_get_type ();
     346            3 :         ctxt->warn
     347            3 :           (std::make_unique<throw_of_unexpected_type> (exception_type,
     348              :                                                        thrown_from_fndecl));
     349            3 :         ctxt->terminate_path ();
     350              :       }
     351            3 :   }
     352              : };
     353              : 
     354              : /* Populate KFM with instances of known functions relating to C++.  */
     355              : 
     356              : void
     357         3377 : register_known_functions_lang_cp (known_function_manager &kfm)
     358              : {
     359         3377 :   kfm.add ("operator new", std::make_unique<kf_operator_new> ());
     360         3377 :   kfm.add ("operator new []", std::make_unique<kf_operator_new> ());
     361         3377 :   kfm.add ("operator delete", std::make_unique<kf_operator_delete> ());
     362         3377 :   kfm.add ("operator delete []", std::make_unique<kf_operator_delete> ());
     363              : 
     364              :   /* Functions mentioned in "Itanium C++ ABI: Exception Handling"'s
     365              :      "Level II: C++ ABI"
     366              :      https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-abi  */
     367         3377 :   kfm.add ("__cxa_allocate_exception",
     368         3377 :            std::make_unique<kf_cxa_allocate_exception> ());
     369              :   // We treat __cxa_throw and __cxa_rethrow as special cases
     370         3377 :   kfm.add ("__cxa_begin_catch", std::make_unique<kf_cxa_begin_catch> ());
     371         3377 :   kfm.add ("__cxa_end_catch", std::make_unique<kf_cxa_end_catch> ());
     372         3377 :   kfm.add ("__cxa_call_unexpected",
     373         3377 :            std::make_unique<kf_cxa_call_unexpected> ());
     374         3377 : }
     375              : 
     376              : } // namespace ana
     377              : 
     378              : #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.