LCOV - code coverage report
Current view: top level - gcc/analyzer - kf-lang-cp.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 100.0 % 65 65
Test Date: 2024-03-23 14:05:01 Functions: 100.0 % 7 7
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-2024 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 "config.h"
      22                 :             : #define INCLUDE_MEMORY
      23                 :             : #include "system.h"
      24                 :             : #include "coretypes.h"
      25                 :             : #include "tree.h"
      26                 :             : #include "function.h"
      27                 :             : #include "basic-block.h"
      28                 :             : #include "gimple.h"
      29                 :             : #include "analyzer/analyzer.h"
      30                 :             : #include "analyzer/analyzer-logging.h"
      31                 :             : #include "diagnostic.h"
      32                 :             : #include "analyzer/region-model.h"
      33                 :             : #include "analyzer/call-details.h"
      34                 :             : #include "make-unique.h"
      35                 :             : 
      36                 :             : #if ENABLE_ANALYZER
      37                 :             : 
      38                 :             : /* Return true if CALL is a non-allocating operator new or operator new []
      39                 :             :    that contains no user-defined args, i.e. having any signature of:
      40                 :             : 
      41                 :             :     - void* operator new (std::size_t count, void* ptr);
      42                 :             :     - void* operator new[] (std::size_t count, void* ptr);
      43                 :             : 
      44                 :             :    See https://en.cppreference.com/w/cpp/memory/new/operator_new.  */
      45                 :             : 
      46                 :       57685 : bool is_placement_new_p (const gcall *call)
      47                 :             : {
      48                 :       57685 :   gcc_assert (call);
      49                 :       57685 :   tree fndecl = gimple_call_fndecl (call);
      50                 :             : 
      51                 :       57685 :   if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
      52                 :             :     /* Give up on overloaded operator new.  */
      53                 :             :     return false;
      54                 :             : 
      55                 :       56581 :   if (!is_named_call_p (fndecl, "operator new", call, 2)
      56                 :       56581 :       && !is_named_call_p (fndecl, "operator new []", call, 2))
      57                 :             :     return false;
      58                 :             : 
      59                 :             :   /* We must distinguish between an allocating non-throwing new
      60                 :             :     and a non-allocating new.
      61                 :             : 
      62                 :             :     The former might have one of the following signatures :
      63                 :             :     void* operator new (std::size_t count, const std::nothrow_t& tag);
      64                 :             :     void* operator new[] (std::size_t count, const std::nothrow_t& tag);
      65                 :             :     Whereas a placement new would take a pointer.  */
      66                 :         972 :   tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
      67                 :         972 :   return TREE_CODE (TREE_VALUE (arg1_type)) == POINTER_TYPE;
      68                 :             : }
      69                 :             : 
      70                 :             : namespace ana {
      71                 :             : 
      72                 :             : /* Implementations of specific functions.  */
      73                 :             : 
      74                 :             : /* Handler for "operator new" and "operator new []".  */
      75                 :             : 
      76                 :        7446 : class kf_operator_new : public known_function
      77                 :             : {
      78                 :             : public:
      79                 :        4116 :   bool matches_call_types_p (const call_details &cd) const final override
      80                 :             :   {
      81                 :        4116 :     return (cd.num_args () == 1
      82                 :        1924 :       && cd.arg_is_size_p (0))
      83                 :        4116 :       || (cd.num_args () == 2
      84                 :        2192 :       && cd.arg_is_size_p (0)
      85                 :        2192 :       && POINTER_TYPE_P (cd.get_arg_type (1)));
      86                 :             :   }
      87                 :             : 
      88                 :        1442 :   void impl_call_pre (const call_details &cd) const final override
      89                 :             :   {
      90                 :        1442 :     region_model *model = cd.get_model ();
      91                 :        1442 :     region_model_manager *mgr = cd.get_manager ();
      92                 :        1442 :     const svalue *size_sval = cd.get_arg_svalue (0);
      93                 :        1442 :     region_model_context *ctxt = cd.get_ctxt ();
      94                 :        1442 :     const gcall *call = cd.get_call_stmt ();
      95                 :             : 
      96                 :             :     /* If the call was actually a placement new, check that accessing
      97                 :             :        the buffer lhs is placed into does not result in out-of-bounds.  */
      98                 :        1442 :     if (is_placement_new_p (call))
      99                 :             :       {
     100                 :         152 :         const region *ptr_reg = cd.deref_ptr_arg (1);
     101                 :         152 :         if (ptr_reg && cd.get_lhs_type ())
     102                 :             :           {
     103                 :         152 :             const svalue *num_bytes_sval = cd.get_arg_svalue (0);
     104                 :         152 :             const region *sized_new_reg
     105                 :         152 :                 = mgr->get_sized_region (ptr_reg,
     106                 :             :                                          cd.get_lhs_type (),
     107                 :             :                                          num_bytes_sval);
     108                 :         152 :             model->check_region_for_write (sized_new_reg,
     109                 :             :                                            nullptr,
     110                 :             :                                            ctxt);
     111                 :         152 :             const svalue *ptr_sval
     112                 :         152 :               = mgr->get_ptr_svalue (cd.get_lhs_type (), sized_new_reg);
     113                 :         152 :             cd.maybe_set_lhs (ptr_sval);
     114                 :             :           }
     115                 :             :       }
     116                 :             :     /* If the call is an allocating new, then create a heap allocated
     117                 :             :        region.  */
     118                 :             :     else
     119                 :             :       {
     120                 :        1290 :         const region *new_reg
     121                 :        1290 :           = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
     122                 :        1290 :         if (cd.get_lhs_type ())
     123                 :             :           {
     124                 :        1290 :             const svalue *ptr_sval
     125                 :        1290 :               = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
     126                 :        1290 :             cd.maybe_set_lhs (ptr_sval);
     127                 :             :           }
     128                 :             :       }
     129                 :        1442 :   }
     130                 :             : 
     131                 :        1442 :   void impl_call_post (const call_details &cd) const final override
     132                 :             :   {
     133                 :        1442 :     region_model *model = cd.get_model ();
     134                 :        1442 :     region_model_manager *mgr = cd.get_manager ();
     135                 :        1442 :     tree callee_fndecl = cd.get_fndecl_for_call ();
     136                 :        1442 :     region_model_context *ctxt = cd.get_ctxt ();
     137                 :             : 
     138                 :             :     /* If the call is guaranteed to return nonnull
     139                 :             :        then add a nonnull constraint to the allocated region.  */
     140                 :        1442 :     if (!TREE_NOTHROW (callee_fndecl) && flag_exceptions)
     141                 :             :       {
     142                 :         358 :         const svalue *null_sval
     143                 :         358 :           = mgr->get_or_create_null_ptr (cd.get_lhs_type ());
     144                 :         358 :         const svalue *result
     145                 :         358 :           = model->get_store_value (cd.get_lhs_region (), ctxt);
     146                 :         358 :         model->add_constraint (result, NE_EXPR, null_sval, ctxt);
     147                 :             :       }
     148                 :        1442 :   }
     149                 :             : };
     150                 :             : 
     151                 :             : /* Handler for "operator delete" and for "operator delete []",
     152                 :             :    both the sized and unsized variants
     153                 :             :    (2 arguments and 1 argument respectively).  */
     154                 :             : 
     155                 :        7446 : class kf_operator_delete : public known_function
     156                 :             : {
     157                 :             : public:
     158                 :        1107 :   bool matches_call_types_p (const call_details &cd) const final override
     159                 :             :   {
     160                 :        1107 :     return cd.num_args () == 1 or cd.num_args () == 2;
     161                 :             :   }
     162                 :             : 
     163                 :         245 :   void impl_call_post (const call_details &cd) const final override
     164                 :             :   {
     165                 :         245 :     region_model *model = cd.get_model ();
     166                 :         245 :     const svalue *ptr_sval = cd.get_arg_svalue (0);
     167                 :         245 :     if (const region *freed_reg = ptr_sval->maybe_get_region ())
     168                 :             :       {
     169                 :             :         /* If the ptr points to an underlying heap region, delete it,
     170                 :             :            poisoning pointers.  */
     171                 :         220 :         model->unbind_region_and_descendents (freed_reg,
     172                 :             :                                               POISON_KIND_DELETED);
     173                 :             :       }
     174                 :         245 :   }
     175                 :             : 
     176                 :             : };
     177                 :             : 
     178                 :             : /* Populate KFM with instances of known functions relating to C++.  */
     179                 :             : 
     180                 :             : void
     181                 :        3723 : register_known_functions_lang_cp (known_function_manager &kfm)
     182                 :             : {
     183                 :        3723 :   kfm.add ("operator new", make_unique<kf_operator_new> ());
     184                 :        3723 :   kfm.add ("operator new []", make_unique<kf_operator_new> ());
     185                 :        3723 :   kfm.add ("operator delete", make_unique<kf_operator_delete> ());
     186                 :        3723 :   kfm.add ("operator delete []", make_unique<kf_operator_delete> ());
     187                 :        3723 : }
     188                 :             : 
     189                 :             : } // namespace ana
     190                 :             : 
     191                 :             : #endif /* #if ENABLE_ANALYZER */
        

Generated by: LCOV version 2.0-1

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.