LCOV - code coverage report
Current view: top level - gcc/analyzer - kf-lang-cp.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 99.3 % 141 140
Test Date: 2025-06-21 16:26:05 Functions: 100.0 % 20 20
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Handling for the known behavior of various functions specific to C++.
       2                 :             :    Copyright (C) 2020-2025 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                 :       50171 : bool is_placement_new_p (const gcall &call)
      40                 :             : {
      41                 :       50171 :   tree fndecl = gimple_call_fndecl (&call);
      42                 :             : 
      43                 :       50171 :   if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
      44                 :             :     /* Give up on overloaded operator new.  */
      45                 :             :     return false;
      46                 :             : 
      47                 :       48759 :   if (!is_named_call_p (fndecl, "operator new", call, 2)
      48                 :       48759 :       && !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                 :         708 :   tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
      59                 :         708 :   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                 :        6626 : class kf_operator_new : public known_function
      69                 :             : {
      70                 :             : public:
      71                 :        3090 :   bool matches_call_types_p (const call_details &cd) const final override
      72                 :             :   {
      73                 :        3090 :     return (cd.num_args () == 1
      74                 :        1512 :       && cd.arg_is_size_p (0))
      75                 :        3090 :       || (cd.num_args () == 2
      76                 :        1578 :       && cd.arg_is_size_p (0)
      77                 :        1578 :       && POINTER_TYPE_P (cd.get_arg_type (1)));
      78                 :             :   }
      79                 :             : 
      80                 :        1095 :   void impl_call_pre (const call_details &cd) const final override
      81                 :             :   {
      82                 :        1095 :     region_model *model = cd.get_model ();
      83                 :        1095 :     region_model_manager *mgr = cd.get_manager ();
      84                 :        1095 :     const svalue *size_sval = cd.get_arg_svalue (0);
      85                 :        1095 :     region_model_context *ctxt = cd.get_ctxt ();
      86                 :        1095 :     const gcall &call = cd.get_call_stmt ();
      87                 :             : 
      88                 :             :     /* If the call was actually a placement new, check that accessing
      89                 :             :        the buffer lhs is placed into does not result in out-of-bounds.  */
      90                 :        1095 :     if (is_placement_new_p (call))
      91                 :             :       {
      92                 :         117 :         const region *ptr_reg = cd.deref_ptr_arg (1);
      93                 :         117 :         if (ptr_reg && cd.get_lhs_type ())
      94                 :             :           {
      95                 :         117 :             const svalue *num_bytes_sval = cd.get_arg_svalue (0);
      96                 :         117 :             const region *sized_new_reg
      97                 :         117 :                 = mgr->get_sized_region (ptr_reg,
      98                 :             :                                          cd.get_lhs_type (),
      99                 :             :                                          num_bytes_sval);
     100                 :         117 :             model->check_region_for_write (sized_new_reg,
     101                 :             :                                            nullptr,
     102                 :             :                                            ctxt);
     103                 :         117 :             const svalue *ptr_sval
     104                 :         117 :               = mgr->get_ptr_svalue (cd.get_lhs_type (), sized_new_reg);
     105                 :         117 :             cd.maybe_set_lhs (ptr_sval);
     106                 :             :           }
     107                 :             :       }
     108                 :             :     /* If the call is an allocating new, then create a heap allocated
     109                 :             :        region.  */
     110                 :             :     else
     111                 :             :       {
     112                 :         978 :         const region *new_reg
     113                 :         978 :           = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
     114                 :         978 :         if (cd.get_lhs_type ())
     115                 :             :           {
     116                 :         978 :             const svalue *ptr_sval
     117                 :         978 :               = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     118                 :         978 :             cd.maybe_set_lhs (ptr_sval);
     119                 :             :           }
     120                 :             :       }
     121                 :        1095 :   }
     122                 :             : 
     123                 :        1095 :   void impl_call_post (const call_details &cd) const final override
     124                 :             :   {
     125                 :        1095 :     region_model *model = cd.get_model ();
     126                 :        1095 :     region_model_manager *mgr = cd.get_manager ();
     127                 :        1095 :     tree callee_fndecl = cd.get_fndecl_for_call ();
     128                 :        1095 :     region_model_context *ctxt = cd.get_ctxt ();
     129                 :             : 
     130                 :             :     /* If the call is guaranteed to return nonnull
     131                 :             :        then add a nonnull constraint to the allocated region.  */
     132                 :        1095 :     if (!TREE_NOTHROW (callee_fndecl) && flag_exceptions)
     133                 :             :       {
     134                 :         291 :         const svalue *null_sval
     135                 :         291 :           = mgr->get_or_create_null_ptr (cd.get_lhs_type ());
     136                 :         291 :         const svalue *result
     137                 :         291 :           = model->get_store_value (cd.get_lhs_region (), ctxt);
     138                 :         291 :         model->add_constraint (result, NE_EXPR, null_sval, ctxt);
     139                 :             :       }
     140                 :        1095 :   }
     141                 :             : };
     142                 :             : 
     143                 :             : /* Handler for "operator delete" and for "operator delete []",
     144                 :             :    both the sized and unsized variants
     145                 :             :    (2 arguments and 1 argument respectively).  */
     146                 :             : 
     147                 :        6626 : class kf_operator_delete : public known_function
     148                 :             : {
     149                 :             : public:
     150                 :        1081 :   bool matches_call_types_p (const call_details &cd) const final override
     151                 :             :   {
     152                 :        1081 :     return cd.num_args () == 1 or cd.num_args () == 2;
     153                 :             :   }
     154                 :             : 
     155                 :         233 :   void impl_call_post (const call_details &cd) const final override
     156                 :             :   {
     157                 :         233 :     region_model *model = cd.get_model ();
     158                 :         233 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     159                 :         233 :     if (const region *freed_reg = ptr_sval->maybe_get_region ())
     160                 :             :       {
     161                 :             :         /* If the ptr points to an underlying heap region, delete it,
     162                 :             :            poisoning pointers.  */
     163                 :         186 :         model->unbind_region_and_descendents (freed_reg,
     164                 :             :                                               poison_kind::deleted);
     165                 :             :       }
     166                 :         233 :   }
     167                 :             : 
     168                 :             : };
     169                 :             : 
     170                 :        3313 : class kf_cxa_allocate_exception : public known_function
     171                 :             : {
     172                 :             : public:
     173                 :         822 :   bool matches_call_types_p (const call_details &cd) const final override
     174                 :             :   {
     175                 :         822 :     return cd.num_args () == 1 && cd.arg_is_size_p (0);
     176                 :             :   }
     177                 :             : 
     178                 :         181 :   void impl_call_pre (const call_details &cd) const final override
     179                 :             :   {
     180                 :         181 :     region_model *model = cd.get_model ();
     181                 :         181 :     region_model_manager *mgr = cd.get_manager ();
     182                 :         181 :     const svalue *size_sval = cd.get_arg_svalue (0);
     183                 :         181 :     region_model_context *ctxt = cd.get_ctxt ();
     184                 :             : 
     185                 :             :     /* Create a heap allocated region.  */
     186                 :         181 :     const region *new_reg
     187                 :         181 :       = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
     188                 :         181 :     if (cd.get_lhs_type ())
     189                 :             :       {
     190                 :         181 :         const svalue *ptr_sval
     191                 :         181 :           = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     192                 :         181 :         cd.maybe_set_lhs (ptr_sval);
     193                 :             :       }
     194                 :         181 :   }
     195                 :             : };
     196                 :             : 
     197                 :        3313 : class kf_cxa_begin_catch : public known_function
     198                 :             : {
     199                 :             : public:
     200                 :         936 :   bool matches_call_types_p (const call_details &cd) const final override
     201                 :             :   {
     202                 :         936 :     return (cd.num_args () == 1
     203                 :         936 :             && POINTER_TYPE_P (cd.get_arg_type (0)));
     204                 :             :   }
     205                 :             : 
     206                 :         174 :   void impl_call_pre (const call_details &cd) const final override
     207                 :             :   {
     208                 :         174 :     region_model *model = cd.get_model ();
     209                 :             : 
     210                 :         174 :     auto node = model->pop_thrown_exception ();
     211                 :         174 :     model->push_caught_exception (node);
     212                 :         174 :     cd.maybe_set_lhs (node.m_exception_sval);
     213                 :         174 :   }
     214                 :             : };
     215                 :             : 
     216                 :        3313 : class kf_cxa_end_catch : public known_function
     217                 :             : {
     218                 :             : public:
     219                 :        1140 :   bool matches_call_types_p (const call_details &cd) const final override
     220                 :             :   {
     221                 :        1140 :     return cd.num_args () == 0;
     222                 :             :   }
     223                 :             : 
     224                 :         204 :   void impl_call_pre (const call_details &cd) const final override
     225                 :             :   {
     226                 :         204 :     region_model *model = cd.get_model ();
     227                 :         204 :     model->pop_caught_exception ();
     228                 :         204 :   }
     229                 :             : };
     230                 :             : 
     231                 :             : /* A subclass of pending_diagnostic for complaining about an exception
     232                 :             :    of an unexpected type being thrown (due to a call to
     233                 :             :    __cxa_call_unexpected).
     234                 :             :    See https://en.cppreference.com/w/cpp/language/except_spec  */
     235                 :             : 
     236                 :             : class throw_of_unexpected_type
     237                 :             : : public pending_diagnostic_subclass<throw_of_unexpected_type>
     238                 :             : {
     239                 :             : public:
     240                 :           3 :   throw_of_unexpected_type (tree exception_type,
     241                 :             :                             tree thrown_from_fndecl)
     242                 :           3 :   : m_exception_type (exception_type),
     243                 :           3 :     m_thrown_from_fndecl (thrown_from_fndecl)
     244                 :             :   {
     245                 :           3 :     gcc_assert (m_exception_type);
     246                 :           3 :     gcc_assert (m_thrown_from_fndecl);
     247                 :           3 :   }
     248                 :             : 
     249                 :          12 :   const char *get_kind () const final override
     250                 :             :   {
     251                 :          12 :     return "throw_of_unexpected_type";
     252                 :             :   }
     253                 :             : 
     254                 :           3 :   bool operator== (const throw_of_unexpected_type &other) const
     255                 :             :   {
     256                 :           3 :     return (m_exception_type == other.m_exception_type
     257                 :           3 :             && m_thrown_from_fndecl == other.m_thrown_from_fndecl);
     258                 :             :   }
     259                 :             : 
     260                 :           6 :   int get_controlling_option () const final override
     261                 :             :   {
     262                 :           6 :     return OPT_Wanalyzer_throw_of_unexpected_type;
     263                 :             :   }
     264                 :             : 
     265                 :           3 :   bool emit (diagnostic_emission_context &ctxt) final override
     266                 :             :   {
     267                 :           3 :     auto_diagnostic_group d;
     268                 :             : 
     269                 :           3 :     bool warned
     270                 :           3 :       = ctxt.warn ("throwing exception of unexpected type %qT from %qE",
     271                 :             :                    m_exception_type, m_thrown_from_fndecl);
     272                 :           3 :     if (warned)
     273                 :             :       {
     274                 :           3 :         inform (DECL_SOURCE_LOCATION (m_thrown_from_fndecl),
     275                 :             :                 "%qE declared here", m_thrown_from_fndecl);
     276                 :             :         // TODO: show specified types?
     277                 :             :       }
     278                 :           6 :     return warned;
     279                 :           3 :   }
     280                 :             : 
     281                 :             :   bool
     282                 :           6 :   describe_final_event (pretty_printer &pp,
     283                 :             :                         const evdesc::final_event &) final override
     284                 :             :   {
     285                 :           6 :     pp_printf  (&pp,
     286                 :             :                 "exception of unexpected type %qT thrown from %qE",
     287                 :             :                 m_exception_type, m_thrown_from_fndecl);
     288                 :           6 :     return true;
     289                 :             :   }
     290                 :             : 
     291                 :             : private:
     292                 :             :   tree m_exception_type;
     293                 :             :   tree m_thrown_from_fndecl;
     294                 :             : };
     295                 :             : 
     296                 :             : /* See https://en.cppreference.com/w/cpp/language/except_spec  */
     297                 :             : 
     298                 :        3313 : class kf_cxa_call_unexpected : public known_function
     299                 :             : {
     300                 :             : public:
     301                 :          15 :   bool matches_call_types_p (const call_details &cd) const final override
     302                 :             :   {
     303                 :          15 :     return (cd.num_args () == 1
     304                 :          15 :             && POINTER_TYPE_P (cd.get_arg_type (0)));
     305                 :             :   }
     306                 :             : 
     307                 :           3 :   void impl_call_pre (const call_details &cd) const final override
     308                 :             :   {
     309                 :           3 :     if (region_model_context *ctxt = cd.get_ctxt ())
     310                 :             :       {
     311                 :           3 :         region_model *model = cd.get_model ();
     312                 :           3 :         tree thrown_from_fndecl = model->get_current_function ()->decl;
     313                 :             :         /* We must have a thrown exception.  */
     314                 :           3 :         auto eh_node = model->get_current_thrown_exception ();
     315                 :           0 :         gcc_assert (eh_node);
     316                 :           3 :         tree exception_type = eh_node->maybe_get_type ();
     317                 :           3 :         ctxt->warn
     318                 :           3 :           (std::make_unique<throw_of_unexpected_type> (exception_type,
     319                 :             :                                                        thrown_from_fndecl));
     320                 :           3 :         ctxt->terminate_path ();
     321                 :             :       }
     322                 :           3 :   }
     323                 :             : };
     324                 :             : 
     325                 :             : /* Populate KFM with instances of known functions relating to C++.  */
     326                 :             : 
     327                 :             : void
     328                 :        3313 : register_known_functions_lang_cp (known_function_manager &kfm)
     329                 :             : {
     330                 :        3313 :   kfm.add ("operator new", std::make_unique<kf_operator_new> ());
     331                 :        3313 :   kfm.add ("operator new []", std::make_unique<kf_operator_new> ());
     332                 :        3313 :   kfm.add ("operator delete", std::make_unique<kf_operator_delete> ());
     333                 :        3313 :   kfm.add ("operator delete []", std::make_unique<kf_operator_delete> ());
     334                 :             : 
     335                 :             :   /* Functions mentioned in "Itanium C++ ABI: Exception Handling"'s
     336                 :             :      "Level II: C++ ABI"
     337                 :             :      https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-abi  */
     338                 :        3313 :   kfm.add ("__cxa_allocate_exception",
     339                 :        3313 :            std::make_unique<kf_cxa_allocate_exception> ());
     340                 :             :   // We treat __cxa_throw and __cxa_rethrow as special cases
     341                 :        3313 :   kfm.add ("__cxa_begin_catch", std::make_unique<kf_cxa_begin_catch> ());
     342                 :        3313 :   kfm.add ("__cxa_end_catch", std::make_unique<kf_cxa_end_catch> ());
     343                 :        3313 :   kfm.add ("__cxa_call_unexpected",
     344                 :        3313 :            std::make_unique<kf_cxa_call_unexpected> ());
     345                 :        3313 : }
     346                 :             : 
     347                 :             : } // namespace ana
     348                 :             : 
     349                 :             : #endif /* #if ENABLE_ANALYZER */
        

Generated by: LCOV version 2.1-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.