LCOV - code coverage report
Current view: top level - gcc/analyzer - sm-malloc.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 91.9 % 1029 946
Test Date: 2025-07-12 13:27:34 Functions: 87.5 % 136 119
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* A state machine for detecting misuses of the malloc/free API.
       2                 :             :    Copyright (C) 2019-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-event-id.h"
      24                 :             : #include "stringpool.h"
      25                 :             : #include "attribs.h"
      26                 :             : #include "xml-printer.h"
      27                 :             : 
      28                 :             : #include "analyzer/analyzer-logging.h"
      29                 :             : #include "analyzer/sm.h"
      30                 :             : #include "analyzer/pending-diagnostic.h"
      31                 :             : #include "analyzer/call-string.h"
      32                 :             : #include "analyzer/program-point.h"
      33                 :             : #include "analyzer/store.h"
      34                 :             : #include "analyzer/region-model.h"
      35                 :             : #include "analyzer/call-details.h"
      36                 :             : #include "analyzer/function-set.h"
      37                 :             : #include "analyzer/program-state.h"
      38                 :             : #include "analyzer/checker-event.h"
      39                 :             : #include "analyzer/exploded-graph.h"
      40                 :             : #include "analyzer/inlining-iterator.h"
      41                 :             : #include "analyzer/ana-state-to-diagnostic-state.h"
      42                 :             : 
      43                 :             : #if ENABLE_ANALYZER
      44                 :             : 
      45                 :             : namespace ana {
      46                 :             : 
      47                 :             : namespace {
      48                 :             : 
      49                 :             : /* This state machine and its various support classes track allocations
      50                 :             :    and deallocations.
      51                 :             : 
      52                 :             :    It has a few standard allocation/deallocation pairs (e.g. new/delete),
      53                 :             :    and also supports user-defined ones via
      54                 :             :    __attribute__ ((malloc(DEALLOCATOR))).
      55                 :             : 
      56                 :             :    There can be more than one valid deallocator for a given allocator,
      57                 :             :    for example:
      58                 :             :      __attribute__ ((malloc (fclose)))
      59                 :             :      __attribute__ ((malloc (freopen, 3)))
      60                 :             :      FILE* fopen (const char*, const char*);
      61                 :             :    A deallocator_set represents a particular set of valid deallocators.
      62                 :             : 
      63                 :             :    We track the expected deallocator_set for a value, but not the allocation
      64                 :             :    function - there could be more than one allocator per deallocator_set.
      65                 :             :    For example, there could be dozens of allocators for "free" beyond just
      66                 :             :    malloc e.g. calloc, xstrdup, etc.  We don't want to explode the number
      67                 :             :    of states by tracking individual allocators in the exploded graph;
      68                 :             :    we merely want to track "this value expects to have 'free' called on it".
      69                 :             :    Perhaps we can reconstruct which allocator was used later, when emitting
      70                 :             :    the path, if it's necessary for precision of wording of diagnostics.  */
      71                 :             : 
      72                 :             : class deallocator;
      73                 :             : class deallocator_set;
      74                 :             : class malloc_state_machine;
      75                 :             : 
      76                 :             : /* An enum for discriminating between different kinds of allocation_state.  */
      77                 :             : 
      78                 :             : enum resource_state
      79                 :             : {
      80                 :             :   /* States that are independent of allocator/deallocator.  */
      81                 :             : 
      82                 :             :   /* The start state.  */
      83                 :             :   RS_START,
      84                 :             : 
      85                 :             :   /* State for a pointer that's been unconditionally dereferenced.  */
      86                 :             :   RS_ASSUMED_NON_NULL,
      87                 :             : 
      88                 :             :   /* State for a pointer that's known to be NULL.  */
      89                 :             :   RS_NULL,
      90                 :             : 
      91                 :             :   /* State for a pointer that's known to not be on the heap (e.g. to a local
      92                 :             :      or global).  */
      93                 :             :   RS_NON_HEAP,
      94                 :             : 
      95                 :             :   /* Stop state, for pointers we don't want to track any more.  */
      96                 :             :   RS_STOP,
      97                 :             : 
      98                 :             :   /* States that relate to a specific deallocator_set.  */
      99                 :             : 
     100                 :             :   /* State for a pointer returned from an allocator that hasn't
     101                 :             :      been checked for NULL.
     102                 :             :      It could be a pointer to heap-allocated memory, or could be NULL.  */
     103                 :             :   RS_UNCHECKED,
     104                 :             : 
     105                 :             :   /* State for a pointer returned from an allocator,
     106                 :             :      known to be non-NULL.  */
     107                 :             :   RS_NONNULL,
     108                 :             : 
     109                 :             :   /* State for a pointer passed to a deallocator.  */
     110                 :             :   RS_FREED
     111                 :             : };
     112                 :             : 
     113                 :             : /* Custom state subclass, which can optionally refer to an a
     114                 :             :    deallocator_set.  */
     115                 :             : 
     116                 :             : struct allocation_state : public state_machine::state
     117                 :             : {
     118                 :       46039 :   allocation_state (const char *name, unsigned id,
     119                 :             :                     enum resource_state rs,
     120                 :             :                     const deallocator_set *deallocators,
     121                 :             :                     const deallocator *deallocator)
     122                 :       46039 :   : state (name, id), m_rs (rs),
     123                 :       43380 :     m_deallocators (deallocators),
     124                 :       43380 :     m_deallocator (deallocator)
     125                 :             :   {}
     126                 :             : 
     127                 :             :   void dump_to_pp (pretty_printer *pp) const override;
     128                 :             : 
     129                 :             :   const allocation_state *get_nonnull () const;
     130                 :             : 
     131                 :             :   enum resource_state m_rs;
     132                 :             :   const deallocator_set *m_deallocators;
     133                 :             :   const deallocator *m_deallocator;
     134                 :             : };
     135                 :             : 
     136                 :             : /* Custom state subclass, for the "assumed-non-null" state
     137                 :             :    where the assumption happens in a particular frame.  */
     138                 :             : 
     139                 :             : struct assumed_non_null_state : public allocation_state
     140                 :             : {
     141                 :        2659 :   assumed_non_null_state (const char *name, unsigned id,
     142                 :             :                           const frame_region *frame)
     143                 :        2659 :   : allocation_state (name, id, RS_ASSUMED_NON_NULL,
     144                 :             :                       nullptr, nullptr),
     145                 :        2659 :     m_frame (frame)
     146                 :             :   {
     147                 :        2659 :     gcc_assert (m_frame);
     148                 :        2659 :   }
     149                 :             : 
     150                 :             :   void dump_to_pp (pretty_printer *pp) const final override;
     151                 :             : 
     152                 :             :   const frame_region *m_frame;
     153                 :             : };
     154                 :             : 
     155                 :             : /* An enum for choosing which wording to use in various diagnostics
     156                 :             :    when describing deallocations.  */
     157                 :             : 
     158                 :             : enum wording
     159                 :             : {
     160                 :             :   WORDING_FREED,
     161                 :             :   WORDING_DELETED,
     162                 :             :   WORDING_DEALLOCATED,
     163                 :             :   WORDING_REALLOCATED
     164                 :             : };
     165                 :             : 
     166                 :             : /* Base class representing a deallocation function,
     167                 :             :    either a built-in one we know about, or one exposed via
     168                 :             :    __attribute__((malloc(DEALLOCATOR))).  */
     169                 :             : 
     170                 :             : struct deallocator
     171                 :             : {
     172                 :             :   hashval_t hash () const;
     173                 :             :   void dump_to_pp (pretty_printer *pp) const;
     174                 :             :   static int cmp (const deallocator *a, const deallocator *b);
     175                 :             :   static int cmp_ptr_ptr (const void *, const void *);
     176                 :             : 
     177                 :             :   /* Name to use in diagnostics.  */
     178                 :             :   const char *m_name;
     179                 :             : 
     180                 :             :   /* Which wording to use in diagnostics.  */
     181                 :             :   enum wording m_wording;
     182                 :             : 
     183                 :             :   /* State for a value passed to one of the deallocators.  */
     184                 :             :   state_machine::state_t m_freed;
     185                 :             : 
     186                 :             : protected:
     187                 :             :   deallocator (malloc_state_machine *sm,
     188                 :             :                const char *name,
     189                 :             :                enum wording wording);
     190                 :             : };
     191                 :             : 
     192                 :             : /* Subclass representing a predefined deallocator.
     193                 :             :    e.g. "delete []", without needing a specific FUNCTION_DECL
     194                 :             :    ahead of time.  */
     195                 :             : 
     196                 :             : struct standard_deallocator : public deallocator
     197                 :             : {
     198                 :             :   standard_deallocator (malloc_state_machine *sm,
     199                 :             :                         const char *name,
     200                 :             :                         enum wording wording);
     201                 :             : };
     202                 :             : 
     203                 :             : /* Subclass representing a user-defined deallocator
     204                 :             :    via __attribute__((malloc(DEALLOCATOR))) given
     205                 :             :    a specific FUNCTION_DECL.  */
     206                 :             : 
     207                 :             : struct custom_deallocator : public deallocator
     208                 :             : {
     209                 :          70 :   custom_deallocator (malloc_state_machine *sm,
     210                 :             :                       tree deallocator_fndecl,
     211                 :             :                       enum wording wording)
     212                 :          70 :   : deallocator (sm, IDENTIFIER_POINTER (DECL_NAME (deallocator_fndecl)),
     213                 :         140 :                  wording)
     214                 :             :   {
     215                 :          70 :   }
     216                 :             : };
     217                 :             : 
     218                 :             : /* Base class representing a set of possible deallocators.
     219                 :             :    Often this will be just a single deallocator, but some
     220                 :             :    allocators have multiple valid deallocators (e.g. the result of
     221                 :             :    "fopen" can be closed by either "fclose" or "freopen").  */
     222                 :             : 
     223                 :             : struct deallocator_set
     224                 :             : {
     225                 :             :   deallocator_set (malloc_state_machine *sm,
     226                 :             :                    enum wording wording);
     227                 :        3320 :   virtual ~deallocator_set () {}
     228                 :             : 
     229                 :             :   virtual bool contains_p (const deallocator *d) const = 0;
     230                 :             :   virtual const deallocator *maybe_get_single () const = 0;
     231                 :             :   virtual void dump_to_pp (pretty_printer *pp) const = 0;
     232                 :             :   void dump () const;
     233                 :             : 
     234                 :             :   /* Which wording to use in diagnostics.  */
     235                 :             :   enum wording m_wording;
     236                 :             : 
     237                 :             :   /* Pointers to states.
     238                 :             :      These states are owned by the state_machine base class.  */
     239                 :             : 
     240                 :             :   /* State for an unchecked result from an allocator using this set.  */
     241                 :             :   state_machine::state_t m_unchecked;
     242                 :             : 
     243                 :             :   /* State for a known non-NULL result from such an allocator.  */
     244                 :             :   state_machine::state_t m_nonnull;
     245                 :             : };
     246                 :             : 
     247                 :             : /* Subclass of deallocator_set representing a set of deallocators
     248                 :             :    defined by one or more __attribute__((malloc(DEALLOCATOR))).  */
     249                 :             : 
     250                 :             : struct custom_deallocator_set : public deallocator_set
     251                 :             : {
     252                 :             :   typedef const auto_vec <const deallocator *> *key_t;
     253                 :             : 
     254                 :             :   custom_deallocator_set (malloc_state_machine *sm,
     255                 :             :                           const auto_vec <const deallocator *> *vec,
     256                 :             :                           //const char *name,
     257                 :             :                           //const char *dealloc_funcname,
     258                 :             :                           //unsigned arg_idx,
     259                 :             :                           enum wording wording);
     260                 :             : 
     261                 :             :   bool contains_p (const deallocator *d) const final override;
     262                 :             :   const deallocator *maybe_get_single () const final override;
     263                 :             :   void dump_to_pp (pretty_printer *pp) const final override;
     264                 :             : 
     265                 :             :   auto_vec <const deallocator *> m_deallocator_vec;
     266                 :             : };
     267                 :             : 
     268                 :             : /* Subclass of deallocator_set representing a set of deallocators
     269                 :             :    with a single standard_deallocator, e.g. "delete []".  */
     270                 :             : 
     271                 :        3320 : struct standard_deallocator_set : public deallocator_set
     272                 :             : {
     273                 :             :   standard_deallocator_set (malloc_state_machine *sm,
     274                 :             :                             const char *name,
     275                 :             :                             enum wording wording);
     276                 :             : 
     277                 :             :   bool contains_p (const deallocator *d) const final override;
     278                 :             :   const deallocator *maybe_get_single () const final override;
     279                 :             :   void dump_to_pp (pretty_printer *pp) const final override;
     280                 :             : 
     281                 :             :   standard_deallocator m_deallocator;
     282                 :             : };
     283                 :             : 
     284                 :             : /* Traits class for ensuring uniqueness of deallocator_sets within
     285                 :             :    malloc_state_machine.  */
     286                 :             : 
     287                 :             : struct deallocator_set_map_traits
     288                 :             : {
     289                 :             :   typedef custom_deallocator_set::key_t key_type;
     290                 :             :   typedef custom_deallocator_set *value_type;
     291                 :             :   typedef custom_deallocator_set *compare_type;
     292                 :             : 
     293                 :         207 :   static inline hashval_t hash (const key_type &k)
     294                 :             :   {
     295                 :         207 :     gcc_assert (k != nullptr);
     296                 :         207 :     gcc_assert (k != reinterpret_cast<key_type> (1));
     297                 :             : 
     298                 :             :     hashval_t result = 0;
     299                 :             :     unsigned i;
     300                 :             :     const deallocator *d;
     301                 :         482 :     FOR_EACH_VEC_ELT (*k, i, d)
     302                 :         275 :       result ^= d->hash ();
     303                 :         207 :     return result;
     304                 :             :   }
     305                 :          44 :   static inline bool equal_keys (const key_type &k1, const key_type &k2)
     306                 :             :   {
     307                 :         132 :     if (k1->length () != k2->length ())
     308                 :             :       return false;
     309                 :             : 
     310                 :          65 :     for (unsigned i = 0; i < k1->length (); i++)
     311                 :          52 :       if ((*k1)[i] != (*k2)[i])
     312                 :             :         return false;
     313                 :             : 
     314                 :             :     return true;
     315                 :             :   }
     316                 :             :   template <typename T>
     317                 :             :   static inline void remove (T &)
     318                 :             :   {
     319                 :             :     /* empty; the nodes are handled elsewhere.  */
     320                 :             :   }
     321                 :             :   template <typename T>
     322                 :             :   static inline void mark_deleted (T &entry)
     323                 :             :   {
     324                 :             :     entry.m_key = reinterpret_cast<key_type> (1);
     325                 :             :   }
     326                 :             :   template <typename T>
     327                 :       43160 :   static inline void mark_empty (T &entry)
     328                 :             :   {
     329                 :       43160 :     entry.m_key = nullptr;
     330                 :             :   }
     331                 :             :   template <typename T>
     332                 :         132 :   static inline bool is_deleted (const T &entry)
     333                 :             :   {
     334                 :         132 :     return entry.m_key == reinterpret_cast<key_type> (1);
     335                 :             :   }
     336                 :             :   template <typename T>
     337                 :       45398 :   static inline bool is_empty (const T &entry)
     338                 :             :   {
     339                 :       45398 :     return entry.m_key == nullptr;
     340                 :             :   }
     341                 :             :   static const bool empty_zero_p = false;
     342                 :             : };
     343                 :             : 
     344                 :             : /* A state machine for detecting misuses of the malloc/free API.
     345                 :             : 
     346                 :             :    See sm-malloc.dot for an overview (keep this in-sync with that file).  */
     347                 :             : 
     348                 :             : class malloc_state_machine : public state_machine
     349                 :             : {
     350                 :             : public:
     351                 :             :   typedef allocation_state custom_data_t;
     352                 :             : 
     353                 :             :   malloc_state_machine (logger *logger);
     354                 :             :   ~malloc_state_machine ();
     355                 :             : 
     356                 :             :   state_t
     357                 :             :   add_state (const char *name, enum resource_state rs,
     358                 :             :              const deallocator_set *deallocators,
     359                 :             :              const deallocator *deallocator);
     360                 :             : 
     361                 :     1550837 :   bool inherited_state_p () const final override { return false; }
     362                 :             : 
     363                 :             :   state_machine::state_t
     364                 :     1418785 :   get_default_state (const svalue *sval) const final override
     365                 :             :   {
     366                 :     1418785 :     if (tree cst = sval->maybe_get_constant ())
     367                 :             :       {
     368                 :      165084 :         if (zerop (cst))
     369                 :       82319 :           return m_null;
     370                 :             :       }
     371                 :     1336466 :     if (const region_svalue *ptr = sval->dyn_cast_region_svalue ())
     372                 :             :       {
     373                 :      201686 :         const region *reg = ptr->get_pointee ();
     374                 :      201686 :         switch (reg->get_memory_space ())
     375                 :             :           {
     376                 :             :           default:
     377                 :             :             break;
     378                 :       26941 :           case MEMSPACE_CODE:
     379                 :       26941 :           case MEMSPACE_GLOBALS:
     380                 :       26941 :           case MEMSPACE_STACK:
     381                 :       26941 :           case MEMSPACE_READONLY_DATA:
     382                 :       26941 :             return m_non_heap;
     383                 :             :           }
     384                 :             :       }
     385                 :     1309525 :     return m_start;
     386                 :             :   }
     387                 :             : 
     388                 :             :   bool on_stmt (sm_context &sm_ctxt,
     389                 :             :                 const supernode *node,
     390                 :             :                 const gimple *stmt) const final override;
     391                 :             : 
     392                 :             :   void on_phi (sm_context &sm_ctxt,
     393                 :             :                const supernode *node,
     394                 :             :                const gphi *phi,
     395                 :             :                tree rhs) const final override;
     396                 :             : 
     397                 :             :   void on_condition (sm_context &sm_ctxt,
     398                 :             :                      const supernode *node,
     399                 :             :                      const gimple *stmt,
     400                 :             :                      const svalue *lhs,
     401                 :             :                      enum tree_code op,
     402                 :             :                      const svalue *rhs) const final override;
     403                 :             : 
     404                 :             :   void on_pop_frame (sm_state_map *smap,
     405                 :             :                      const frame_region *) const final override;
     406                 :             : 
     407                 :             :   bool can_purge_p (state_t s) const final override;
     408                 :             : 
     409                 :             :   std::unique_ptr<pending_diagnostic>
     410                 :             :   on_leak (tree var,
     411                 :             :            const program_state *old_state,
     412                 :             :            const program_state *new_state) const final override;
     413                 :             : 
     414                 :             :   bool reset_when_passed_to_unknown_fn_p (state_t s,
     415                 :             :                                           bool is_mutable) const final override;
     416                 :             : 
     417                 :             :   state_t
     418                 :             :   maybe_get_merged_states_nonequal (state_t state_a,
     419                 :             :                                     state_t state_b) const final override;
     420                 :             : 
     421                 :             :   static bool unaffected_by_call_p (tree fndecl);
     422                 :             : 
     423                 :             :   void maybe_assume_non_null (sm_context &sm_ctxt,
     424                 :             :                               tree ptr,
     425                 :             :                               const gimple *stmt) const;
     426                 :             : 
     427                 :             :   void on_realloc_with_move (region_model *model,
     428                 :             :                              sm_state_map *smap,
     429                 :             :                              const svalue *old_ptr_sval,
     430                 :             :                              const svalue *new_ptr_sval,
     431                 :             :                              const extrinsic_state &ext_state) const;
     432                 :             : 
     433                 :             :   void transition_ptr_sval_non_null (region_model *model,
     434                 :             :       sm_state_map *smap,
     435                 :             :       const svalue *new_ptr_sval,
     436                 :             :       const extrinsic_state &ext_state) const;
     437                 :             : 
     438                 :             :   void
     439                 :             :   add_state_to_state_graph (analyzer_state_graph &out_state_graph,
     440                 :             :                             const svalue &sval,
     441                 :             :                             state_machine::state_t state) const final override;
     442                 :             : 
     443                 :             :   standard_deallocator_set m_free;
     444                 :             :   standard_deallocator_set m_scalar_delete;
     445                 :             :   standard_deallocator_set m_vector_delete;
     446                 :             : 
     447                 :             :   standard_deallocator m_realloc;
     448                 :             : 
     449                 :             :   /* States that are independent of api.  */
     450                 :             : 
     451                 :             :   /* States for a pointer that's been unconditionally dereferenced
     452                 :             :      in a particular stack frame.  */
     453                 :             :   hash_map<const frame_region *, state_t> m_assumed_non_null;
     454                 :             : 
     455                 :             :   /* State for a pointer that's known to be NULL.  */
     456                 :             :   state_t m_null;
     457                 :             : 
     458                 :             :   /* State for a pointer that's known to not be on the heap (e.g. to a local
     459                 :             :      or global).  */
     460                 :             :   state_t m_non_heap; // TODO: or should this be a different state machine?
     461                 :             :   // or do we need child values etc?
     462                 :             : 
     463                 :             :   /* Stop state, for pointers we don't want to track any more.  */
     464                 :             :   state_t m_stop;
     465                 :             : 
     466                 :             : private:
     467                 :             :   const custom_deallocator_set *
     468                 :             :   get_or_create_custom_deallocator_set (tree allocator_fndecl);
     469                 :             :   custom_deallocator_set *
     470                 :             :   maybe_create_custom_deallocator_set (tree allocator_fndecl);
     471                 :             :   const deallocator *
     472                 :             :   get_or_create_deallocator (tree deallocator_fndecl);
     473                 :             : 
     474                 :             :   state_t
     475                 :             :   get_or_create_assumed_non_null_state_for_frame (const frame_region *frame);
     476                 :             : 
     477                 :             :   void
     478                 :             :   maybe_complain_about_deref_before_check (sm_context &sm_ctxt,
     479                 :             :                                            const supernode *node,
     480                 :             :                                            const gimple *stmt,
     481                 :             :                                            const assumed_non_null_state *,
     482                 :             :                                            tree ptr) const;
     483                 :             : 
     484                 :             :   void on_allocator_call (sm_context &sm_ctxt,
     485                 :             :                           const gcall &call,
     486                 :             :                           const deallocator_set *deallocators,
     487                 :             :                           bool returns_nonnull = false) const;
     488                 :             :   void handle_free_of_non_heap (sm_context &sm_ctxt,
     489                 :             :                                 const supernode *node,
     490                 :             :                                 const gcall &call,
     491                 :             :                                 tree arg,
     492                 :             :                                 const deallocator *d) const;
     493                 :             :   void on_deallocator_call (sm_context &sm_ctxt,
     494                 :             :                             const supernode *node,
     495                 :             :                             const gcall &call,
     496                 :             :                             const deallocator *d,
     497                 :             :                             unsigned argno) const;
     498                 :             :   void on_realloc_call (sm_context &sm_ctxt,
     499                 :             :                         const supernode *node,
     500                 :             :                         const gcall &call) const;
     501                 :             :   void on_zero_assignment (sm_context &sm_ctxt,
     502                 :             :                            const gimple *stmt,
     503                 :             :                            tree lhs) const;
     504                 :             :   void handle_nonnull (sm_context &sm_ctx,
     505                 :             :                        const supernode *node,
     506                 :             :                        const gimple *stmt,
     507                 :             :                        tree fndecl,
     508                 :             :                        tree arg,
     509                 :             :                        unsigned i) const;
     510                 :             : 
     511                 :             :   /* A map for consolidating deallocators so that they are
     512                 :             :      unique per deallocator FUNCTION_DECL.  */
     513                 :             :   typedef hash_map<tree, deallocator *> deallocator_map_t;
     514                 :             :   deallocator_map_t m_deallocator_map;
     515                 :             : 
     516                 :             :   /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *.  */
     517                 :             :   typedef hash_map<tree, custom_deallocator_set *> deallocator_set_cache_t;
     518                 :             :   deallocator_set_cache_t m_custom_deallocator_set_cache;
     519                 :             : 
     520                 :             :   /* A map for consolidating custom_deallocator_set instances.  */
     521                 :             :   typedef hash_map<custom_deallocator_set::key_t,
     522                 :             :                    custom_deallocator_set *,
     523                 :             :                    deallocator_set_map_traits> custom_deallocator_set_map_t;
     524                 :             :   custom_deallocator_set_map_t m_custom_deallocator_set_map;
     525                 :             : 
     526                 :             :   /* Record of dynamically-allocated objects,  for cleanup.  */
     527                 :             :   auto_vec <custom_deallocator_set *> m_dynamic_sets;
     528                 :             :   auto_vec <custom_deallocator *> m_dynamic_deallocators;
     529                 :             : };
     530                 :             : 
     531                 :             : /* struct deallocator.  */
     532                 :             : 
     533                 :       13350 : deallocator::deallocator (malloc_state_machine *sm,
     534                 :             :                           const char *name,
     535                 :       13350 :                           enum wording wording)
     536                 :       13350 : : m_name (name),
     537                 :       13350 :   m_wording (wording),
     538                 :       13350 :   m_freed (sm->add_state ("freed", RS_FREED, nullptr, this))
     539                 :             : {
     540                 :       13350 : }
     541                 :             : 
     542                 :             : hashval_t
     543                 :         275 : deallocator::hash () const
     544                 :             : {
     545                 :         275 :   return (hashval_t)m_freed->get_id ();
     546                 :             : }
     547                 :             : 
     548                 :             : void
     549                 :           0 : deallocator::dump_to_pp (pretty_printer *pp) const
     550                 :             : {
     551                 :           0 :   pp_printf (pp, "%qs", m_name);
     552                 :           0 : }
     553                 :             : 
     554                 :             : int
     555                 :          80 : deallocator::cmp (const deallocator *a, const deallocator *b)
     556                 :             : {
     557                 :          80 :   return (int)a->m_freed->get_id () - (int)b->m_freed->get_id ();
     558                 :             : }
     559                 :             : 
     560                 :             : int
     561                 :          80 : deallocator::cmp_ptr_ptr (const void *a, const void *b)
     562                 :             : {
     563                 :          80 :   return cmp (*(const deallocator * const *)a,
     564                 :          80 :               *(const deallocator * const *)b);
     565                 :             : }
     566                 :             : 
     567                 :             : 
     568                 :             : /* struct standard_deallocator : public deallocator.  */
     569                 :             : 
     570                 :       13280 : standard_deallocator::standard_deallocator (malloc_state_machine *sm,
     571                 :             :                                             const char *name,
     572                 :       13280 :                                             enum wording wording)
     573                 :           0 : : deallocator (sm, name, wording)
     574                 :             : {
     575                 :           0 : }
     576                 :             : 
     577                 :             : /* struct deallocator_set.  */
     578                 :             : 
     579                 :       10035 : deallocator_set::deallocator_set (malloc_state_machine *sm,
     580                 :       10035 :                                   enum wording wording)
     581                 :       10035 : : m_wording (wording),
     582                 :       20070 :   m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, nullptr)),
     583                 :       10035 :   m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, nullptr))
     584                 :             : {
     585                 :       10035 : }
     586                 :             : 
     587                 :             : /* Dump a description of this deallocator_set to stderr.  */
     588                 :             : 
     589                 :             : DEBUG_FUNCTION void
     590                 :           0 : deallocator_set::dump () const
     591                 :             : {
     592                 :           0 :   tree_dump_pretty_printer pp (stderr);
     593                 :           0 :   dump_to_pp (&pp);
     594                 :           0 :   pp_newline (&pp);
     595                 :           0 : }
     596                 :             : 
     597                 :             : /* struct custom_deallocator_set : public deallocator_set.  */
     598                 :             : 
     599                 :          75 : custom_deallocator_set::
     600                 :             : custom_deallocator_set (malloc_state_machine *sm,
     601                 :             :                         const auto_vec <const deallocator *> *vec,
     602                 :          75 :                         enum wording wording)
     603                 :             : : deallocator_set (sm, wording),
     604                 :         150 :   m_deallocator_vec (vec->length ())
     605                 :             : {
     606                 :          75 :   unsigned i;
     607                 :          75 :   const deallocator *d;
     608                 :         233 :   FOR_EACH_VEC_ELT (*vec, i, d)
     609                 :          83 :     m_deallocator_vec.safe_push (d);
     610                 :          75 : }
     611                 :             : 
     612                 :             : bool
     613                 :         208 : custom_deallocator_set::contains_p (const deallocator *d) const
     614                 :             : {
     615                 :         208 :   unsigned i;
     616                 :         208 :   const deallocator *cd;
     617                 :         325 :   FOR_EACH_VEC_ELT (m_deallocator_vec, i, cd)
     618                 :         264 :     if (cd == d)
     619                 :             :       return true;
     620                 :             :   return false;
     621                 :             : }
     622                 :             : 
     623                 :             : const deallocator *
     624                 :         305 : custom_deallocator_set::maybe_get_single () const
     625                 :             : {
     626                 :         305 :   if (m_deallocator_vec.length () == 1)
     627                 :         125 :     return m_deallocator_vec[0];
     628                 :             :   return nullptr;
     629                 :             : }
     630                 :             : 
     631                 :             : void
     632                 :           0 : custom_deallocator_set::dump_to_pp (pretty_printer *pp) const
     633                 :             : {
     634                 :           0 :   pp_character (pp, '{');
     635                 :           0 :   unsigned i;
     636                 :           0 :   const deallocator *d;
     637                 :           0 :   FOR_EACH_VEC_ELT (m_deallocator_vec, i, d)
     638                 :             :     {
     639                 :           0 :       if (i > 0)
     640                 :           0 :         pp_string (pp, ", ");
     641                 :           0 :       d->dump_to_pp (pp);
     642                 :             :     }
     643                 :           0 :   pp_character (pp, '}');
     644                 :           0 : }
     645                 :             : 
     646                 :             : /* struct standard_deallocator_set : public deallocator_set.  */
     647                 :             : 
     648                 :        9960 : standard_deallocator_set::standard_deallocator_set (malloc_state_machine *sm,
     649                 :             :                                                     const char *name,
     650                 :        9960 :                                                     enum wording wording)
     651                 :             : : deallocator_set (sm, wording),
     652                 :        9960 :   m_deallocator (sm, name, wording)
     653                 :             : {
     654                 :        9960 : }
     655                 :             : 
     656                 :             : bool
     657                 :        5805 : standard_deallocator_set::contains_p (const deallocator *d) const
     658                 :             : {
     659                 :        5805 :   return d == &m_deallocator;
     660                 :             : }
     661                 :             : 
     662                 :             : const deallocator *
     663                 :         111 : standard_deallocator_set::maybe_get_single () const
     664                 :             : {
     665                 :         111 :   return &m_deallocator;
     666                 :             : }
     667                 :             : 
     668                 :             : void
     669                 :         462 : standard_deallocator_set::dump_to_pp (pretty_printer *pp) const
     670                 :             : {
     671                 :         462 :   pp_character (pp, '{');
     672                 :         462 :   pp_string (pp, m_deallocator.m_name);
     673                 :         462 :   pp_character (pp, '}');
     674                 :         462 : }
     675                 :             : 
     676                 :             : /* Return STATE cast to the custom state subclass, or nullptr for the
     677                 :             :    start state.
     678                 :             :    Everything should be an allocation_state apart from the start state.  */
     679                 :             : 
     680                 :             : static const allocation_state *
     681                 :     1434286 : dyn_cast_allocation_state (state_machine::state_t state)
     682                 :             : {
     683                 :           0 :   if (state->get_id () == 0)
     684                 :           0 :     return nullptr;
     685                 :             :   return static_cast <const allocation_state *> (state);
     686                 :             : }
     687                 :             : 
     688                 :             : /* Return STATE cast to the custom state subclass, for a state that is
     689                 :             :    already known to not be the start state .  */
     690                 :             : 
     691                 :             : static const allocation_state *
     692                 :       10703 : as_a_allocation_state (state_machine::state_t state)
     693                 :             : {
     694                 :       10703 :   gcc_assert (state->get_id () != 0);
     695                 :       10703 :   return static_cast <const allocation_state *> (state);
     696                 :             : }
     697                 :             : 
     698                 :             : /* Get the resource_state for STATE.  */
     699                 :             : 
     700                 :             : static enum resource_state
     701                 :     1434286 : get_rs (state_machine::state_t state)
     702                 :             : {
     703                 :           0 :   if (const allocation_state *astate = dyn_cast_allocation_state (state))
     704                 :      194204 :     return astate->m_rs;
     705                 :             :   else
     706                 :             :     return RS_START;
     707                 :             : }
     708                 :             : 
     709                 :             : /* Return true if STATE is the start state.  */
     710                 :             : 
     711                 :             : static bool
     712                 :         389 : start_p (state_machine::state_t state)
     713                 :             : {
     714                 :           0 :   return get_rs (state) == RS_START;
     715                 :             : }
     716                 :             : 
     717                 :             : /* Return true if STATE is an unchecked result from an allocator.  */
     718                 :             : 
     719                 :             : static bool
     720                 :       42889 : unchecked_p (state_machine::state_t state)
     721                 :             : {
     722                 :           0 :   return get_rs (state) == RS_UNCHECKED;
     723                 :             : }
     724                 :             : 
     725                 :             : /* Return true if STATE is a non-null result from an allocator.  */
     726                 :             : 
     727                 :             : static bool
     728                 :        7050 : nonnull_p (state_machine::state_t state)
     729                 :             : {
     730                 :           0 :   return get_rs (state) == RS_NONNULL;
     731                 :             : }
     732                 :             : 
     733                 :             : /* Return true if STATE is a value that has been passed to a deallocator.  */
     734                 :             : 
     735                 :             : static bool
     736                 :       13556 : freed_p (state_machine::state_t state)
     737                 :             : {
     738                 :           0 :   return get_rs (state) == RS_FREED;
     739                 :             : }
     740                 :             : 
     741                 :             : /* Return true if STATE is a value that has been assumed to be non-NULL.  */
     742                 :             : 
     743                 :             : static bool
     744                 :      260407 : assumed_non_null_p (state_machine::state_t state)
     745                 :             : {
     746                 :       61244 :   return get_rs (state) == RS_ASSUMED_NON_NULL;
     747                 :             : }
     748                 :             : 
     749                 :             : /* Class for diagnostics relating to malloc_state_machine.  */
     750                 :             : 
     751                 :           0 : class malloc_diagnostic : public pending_diagnostic
     752                 :             : {
     753                 :             : public:
     754                 :        7480 :   malloc_diagnostic (const malloc_state_machine &sm, tree arg)
     755                 :        7480 :   : m_sm (sm), m_arg (arg)
     756                 :             :   {}
     757                 :             : 
     758                 :        2219 :   bool subclass_equal_p (const pending_diagnostic &base_other) const override
     759                 :             :   {
     760                 :        2219 :     return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
     761                 :             :   }
     762                 :             : 
     763                 :             :   bool
     764                 :        1108 :   describe_state_change (pretty_printer &pp,
     765                 :             :                          const evdesc::state_change &change) override
     766                 :             :   {
     767                 :        1108 :     if (change.m_old_state == m_sm.get_start_state ()
     768                 :        1108 :         && (unchecked_p (change.m_new_state) || nonnull_p (change.m_new_state)))
     769                 :             :       // TODO: verify that it's the allocation stmt, not a copy
     770                 :             :       {
     771                 :         426 :         pp_string (&pp, "allocated here");
     772                 :         426 :         return true;
     773                 :             :       }
     774                 :         682 :     if (unchecked_p (change.m_old_state)
     775                 :         370 :         && nonnull_p (change.m_new_state))
     776                 :             :       {
     777                 :         346 :         if (change.m_expr)
     778                 :         320 :           pp_printf (&pp, "assuming %qE is non-NULL",
     779                 :             :                      change.m_expr);
     780                 :             :         else
     781                 :          26 :           pp_printf (&pp, "assuming %qs is non-NULL",
     782                 :             :                      "<unknown>");
     783                 :         346 :         return true;
     784                 :             :       }
     785                 :         336 :     if (change.m_new_state == m_sm.m_null)
     786                 :             :       {
     787                 :         334 :         if (unchecked_p (change.m_old_state))
     788                 :             :           {
     789                 :          24 :             if (change.m_expr)
     790                 :          24 :               pp_printf (&pp, "assuming %qE is NULL",
     791                 :             :                          change.m_expr);
     792                 :             :             else
     793                 :           0 :               pp_printf (&pp, "assuming %qs is NULL",
     794                 :             :                          "<unknown>");
     795                 :          24 :             return true;
     796                 :             :           }
     797                 :             :         else
     798                 :             :           {
     799                 :         310 :             if (change.m_expr)
     800                 :             :               {
     801                 :         310 :                 if (zerop (change.m_expr))
     802                 :         114 :                   pp_printf (&pp, "using NULL here");
     803                 :             :                 else
     804                 :         196 :                   pp_printf (&pp, "%qE is NULL",
     805                 :         196 :                              change.m_expr);
     806                 :             :               }
     807                 :             :             else
     808                 :           0 :               pp_printf (&pp, "%qs is NULL",
     809                 :             :                          "<unknown>");
     810                 :         310 :             return true;
     811                 :             :           }
     812                 :             :       }
     813                 :             : 
     814                 :             :     return false;
     815                 :             :   }
     816                 :             : 
     817                 :             :   diagnostic_event::meaning
     818                 :         198 :   get_meaning_for_state_change (const evdesc::state_change &change)
     819                 :             :     const final override
     820                 :             :   {
     821                 :         198 :     if (change.m_old_state == m_sm.get_start_state ()
     822                 :         198 :         && unchecked_p (change.m_new_state))
     823                 :          58 :       return diagnostic_event::meaning (diagnostic_event::verb::acquire,
     824                 :          58 :                                         diagnostic_event::noun::memory);
     825                 :         140 :     if (freed_p (change.m_new_state))
     826                 :         123 :       return diagnostic_event::meaning (diagnostic_event::verb::release,
     827                 :         123 :                                         diagnostic_event::noun::memory);
     828                 :          17 :     return diagnostic_event::meaning ();
     829                 :             :   }
     830                 :             : 
     831                 :             : protected:
     832                 :             :   const malloc_state_machine &m_sm;
     833                 :             :   tree m_arg;
     834                 :             : };
     835                 :             : 
     836                 :             : /* Concrete subclass for reporting mismatching allocator/deallocator
     837                 :             :    diagnostics.  */
     838                 :             : 
     839                 :           0 : class mismatching_deallocation : public malloc_diagnostic
     840                 :             : {
     841                 :             : public:
     842                 :          94 :   mismatching_deallocation (const malloc_state_machine &sm, tree arg,
     843                 :             :                             const deallocator_set *expected_deallocators,
     844                 :             :                             const deallocator *actual_dealloc)
     845                 :          94 :   : malloc_diagnostic (sm, arg),
     846                 :          94 :     m_expected_deallocators (expected_deallocators),
     847                 :          94 :     m_actual_dealloc (actual_dealloc)
     848                 :             :   {}
     849                 :             : 
     850                 :        1782 :   const char *get_kind () const final override
     851                 :             :   {
     852                 :        1782 :     return "mismatching_deallocation";
     853                 :             :   }
     854                 :             : 
     855                 :         182 :   int get_controlling_option () const final override
     856                 :             :   {
     857                 :         182 :     return OPT_Wanalyzer_mismatching_deallocation;
     858                 :             :   }
     859                 :             : 
     860                 :          88 :   bool emit (diagnostic_emission_context &ctxt) final override
     861                 :             :   {
     862                 :          88 :     auto_diagnostic_group d;
     863                 :          88 :     ctxt.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines.  */
     864                 :         176 :     if (const deallocator *expected_dealloc
     865                 :          88 :           = m_expected_deallocators->maybe_get_single ())
     866                 :          52 :       return ctxt.warn ("%qE should have been deallocated with %qs"
     867                 :             :                         " but was deallocated with %qs",
     868                 :          52 :                         m_arg, expected_dealloc->m_name,
     869                 :          52 :                         m_actual_dealloc->m_name);
     870                 :             :     else
     871                 :          36 :       return ctxt.warn ("%qs called on %qE returned from a mismatched"
     872                 :             :                         " allocation function",
     873                 :          36 :                         m_actual_dealloc->m_name, m_arg);
     874                 :          88 :   }
     875                 :             : 
     876                 :             :   bool
     877                 :         206 :   describe_state_change (pretty_printer &pp,
     878                 :             :                          const evdesc::state_change &change) final override
     879                 :             :   {
     880                 :         206 :     if (unchecked_p (change.m_new_state))
     881                 :             :       {
     882                 :         176 :         m_alloc_event = change.m_event_id;
     883                 :         352 :         if (const deallocator *expected_dealloc
     884                 :         176 :             = m_expected_deallocators->maybe_get_single ())
     885                 :         104 :           pp_printf (&pp,
     886                 :             :                      "allocated here (expects deallocation with %qs)",
     887                 :         104 :                      expected_dealloc->m_name);
     888                 :             :         else
     889                 :          72 :           pp_string (&pp, "allocated here");
     890                 :         176 :         return true;
     891                 :             :       }
     892                 :          30 :     return malloc_diagnostic::describe_state_change (pp, change);
     893                 :             :   }
     894                 :             : 
     895                 :             :   bool
     896                 :         176 :   describe_final_event (pretty_printer &pp,
     897                 :             :                         const evdesc::final_event &) final override
     898                 :             :   {
     899                 :         176 :     if (m_alloc_event.known_p ())
     900                 :             :       {
     901                 :         304 :         if (const deallocator *expected_dealloc
     902                 :         152 :             = m_expected_deallocators->maybe_get_single ())
     903                 :          80 :           pp_printf (&pp,
     904                 :             :                      "deallocated with %qs here;"
     905                 :             :                      " allocation at %@ expects deallocation with %qs",
     906                 :          80 :                      m_actual_dealloc->m_name, &m_alloc_event,
     907                 :          80 :                      expected_dealloc->m_name);
     908                 :             :         else
     909                 :          72 :           pp_printf (&pp,
     910                 :             :                      "deallocated with %qs here;"
     911                 :             :                      " allocated at %@",
     912                 :          72 :                      m_actual_dealloc->m_name, &m_alloc_event);
     913                 :         152 :         return true;
     914                 :             :       }
     915                 :          24 :     pp_printf (&pp, "deallocated with %qs here",
     916                 :          24 :                m_actual_dealloc->m_name);
     917                 :          24 :     return true;
     918                 :             :   }
     919                 :             : 
     920                 :             : private:
     921                 :             :   diagnostic_event_id_t m_alloc_event;
     922                 :             :   const deallocator_set *m_expected_deallocators;
     923                 :             :   const deallocator *m_actual_dealloc;
     924                 :             : };
     925                 :             : 
     926                 :             : /* Concrete subclass for reporting double-free diagnostics.  */
     927                 :             : 
     928                 :           0 : class double_free : public malloc_diagnostic
     929                 :             : {
     930                 :             : public:
     931                 :        4477 :   double_free (const malloc_state_machine &sm, tree arg, const char *funcname)
     932                 :        4477 :   : malloc_diagnostic (sm, arg), m_funcname (funcname)
     933                 :             :   {}
     934                 :             : 
     935                 :        4412 :   const char *get_kind () const final override { return "double_free"; }
     936                 :             : 
     937                 :        4865 :   int get_controlling_option () const final override
     938                 :             :   {
     939                 :        4865 :     return OPT_Wanalyzer_double_free;
     940                 :             :   }
     941                 :             : 
     942                 :         388 :   bool emit (diagnostic_emission_context &ctxt) final override
     943                 :             :   {
     944                 :         388 :     auto_diagnostic_group d;
     945                 :         388 :     ctxt.add_cwe (415); /* CWE-415: Double Free.  */
     946                 :         388 :     return ctxt.warn ("double-%qs of %qE", m_funcname, m_arg);
     947                 :         388 :   }
     948                 :             : 
     949                 :             :   bool
     950                 :        1048 :   describe_state_change (pretty_printer &pp,
     951                 :             :                          const evdesc::state_change &change) final override
     952                 :             :   {
     953                 :        1048 :     if (freed_p (change.m_new_state))
     954                 :             :       {
     955                 :         780 :         m_first_free_event = change.m_event_id;
     956                 :         780 :         pp_printf (&pp, "first %qs here", m_funcname);
     957                 :         780 :         return true;
     958                 :             :       }
     959                 :         268 :     return malloc_diagnostic::describe_state_change (pp, change);
     960                 :             :   }
     961                 :             : 
     962                 :             :   bool
     963                 :         300 :   describe_call_with_state (pretty_printer &pp,
     964                 :             :                             const evdesc::call_with_state &info) final override
     965                 :             :   {
     966                 :         300 :     if (freed_p (info.m_state))
     967                 :             :       {
     968                 :         148 :         pp_printf (&pp,
     969                 :             :                    "passing freed pointer %qE in call to %qE from %qE",
     970                 :         148 :                    info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
     971                 :         148 :         return true;
     972                 :             :       }
     973                 :             :     return false;
     974                 :             :   }
     975                 :             : 
     976                 :             :   bool
     977                 :         772 :   describe_final_event (pretty_printer &pp,
     978                 :             :                         const evdesc::final_event &) final override
     979                 :             :   {
     980                 :         772 :     if (m_first_free_event.known_p ())
     981                 :         772 :       pp_printf (&pp,
     982                 :             :                  "second %qs here; first %qs was at %@",
     983                 :             :                  m_funcname, m_funcname,
     984                 :             :                  &m_first_free_event);
     985                 :             :     else
     986                 :           0 :       pp_printf (&pp, "second %qs here", m_funcname);
     987                 :         772 :     return true;
     988                 :             :   }
     989                 :             : 
     990                 :             : private:
     991                 :             :   diagnostic_event_id_t m_first_free_event;
     992                 :             :   const char *m_funcname;
     993                 :             : };
     994                 :             : 
     995                 :             : /* Abstract subclass for describing possible bad uses of NULL.
     996                 :             :    Responsible for describing the call that could return NULL.  */
     997                 :             : 
     998                 :           0 : class possible_null : public malloc_diagnostic
     999                 :             : {
    1000                 :             : public:
    1001                 :        1113 :   possible_null (const malloc_state_machine &sm, tree arg)
    1002                 :        1113 :   : malloc_diagnostic (sm, arg)
    1003                 :             :   {}
    1004                 :             : 
    1005                 :             :   bool
    1006                 :         380 :   describe_state_change (pretty_printer &pp,
    1007                 :             :                          const evdesc::state_change &change) final override
    1008                 :             :   {
    1009                 :         380 :     if (change.m_old_state == m_sm.get_start_state ()
    1010                 :         380 :         && unchecked_p (change.m_new_state))
    1011                 :             :       {
    1012                 :         380 :         m_origin_of_unchecked_event = change.m_event_id;
    1013                 :         380 :         pp_string (&pp, "this call could return NULL");
    1014                 :         380 :         return true;
    1015                 :             :       }
    1016                 :           0 :     return malloc_diagnostic::describe_state_change (pp, change);
    1017                 :             :   }
    1018                 :             : 
    1019                 :             :   bool
    1020                 :          26 :   describe_return_of_state (pretty_printer &pp,
    1021                 :             :                             const evdesc::return_of_state &info) final override
    1022                 :             :   {
    1023                 :          26 :     if (unchecked_p (info.m_state))
    1024                 :             :       {
    1025                 :          26 :         pp_printf (&pp,
    1026                 :             :                    "possible return of NULL to %qE from %qE",
    1027                 :          26 :                    info.m_caller_fndecl, info.m_callee_fndecl);
    1028                 :          26 :         return true;
    1029                 :             :       }
    1030                 :             :     return false;
    1031                 :             :   }
    1032                 :             : 
    1033                 :             : protected:
    1034                 :             :   diagnostic_event_id_t m_origin_of_unchecked_event;
    1035                 :             : };
    1036                 :             : 
    1037                 :             : /* Concrete subclass for describing dereference of a possible NULL
    1038                 :             :    value.  */
    1039                 :             : 
    1040                 :           0 : class possible_null_deref : public possible_null
    1041                 :             : {
    1042                 :             : public:
    1043                 :        1040 :   possible_null_deref (const malloc_state_machine &sm, tree arg)
    1044                 :        1040 :   : possible_null (sm, arg)
    1045                 :             :   {}
    1046                 :             : 
    1047                 :        2986 :   const char *get_kind () const final override { return "possible_null_deref"; }
    1048                 :             : 
    1049                 :         434 :   int get_controlling_option () const final override
    1050                 :             :   {
    1051                 :         434 :     return OPT_Wanalyzer_possible_null_dereference;
    1052                 :             :   }
    1053                 :             : 
    1054                 :         144 :   bool emit (diagnostic_emission_context &ctxt) final override
    1055                 :             :   {
    1056                 :             :     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
    1057                 :         144 :     ctxt.add_cwe (690);
    1058                 :         144 :     return ctxt.warn ("dereference of possibly-NULL %qE", m_arg);
    1059                 :             :   }
    1060                 :             : 
    1061                 :             :   bool
    1062                 :         288 :   describe_final_event (pretty_printer &pp,
    1063                 :             :                         const evdesc::final_event &ev) final override
    1064                 :             :   {
    1065                 :         288 :     if (m_origin_of_unchecked_event.known_p ())
    1066                 :         288 :       pp_printf (&pp,
    1067                 :             :                  "%qE could be NULL: unchecked value from %@",
    1068                 :         288 :                  ev.m_expr,
    1069                 :             :                  &m_origin_of_unchecked_event);
    1070                 :             :     else
    1071                 :           0 :       pp_printf (&pp,"%qE could be NULL", ev.m_expr);
    1072                 :         288 :     return true;
    1073                 :             :   }
    1074                 :             : 
    1075                 :             : };
    1076                 :             : 
    1077                 :             : /* Return true if FNDECL is a C++ method.  */
    1078                 :             : 
    1079                 :             : static bool
    1080                 :         666 : method_p (tree fndecl)
    1081                 :             : {
    1082                 :         666 :   return TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE;
    1083                 :             : }
    1084                 :             : 
    1085                 :             : /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
    1086                 :             :    Compare with %P in the C++ FE  (implemented in cp/error.cc: parm_to_string
    1087                 :             :    as called from cp_printer).  */
    1088                 :             : 
    1089                 :             : static label_text
    1090                 :         342 : describe_argument_index (tree fndecl, int arg_idx)
    1091                 :             : {
    1092                 :         342 :   if (method_p (fndecl))
    1093                 :          36 :     if (arg_idx == 0)
    1094                 :          18 :       return label_text::borrow ("'this'");
    1095                 :         324 :   pretty_printer pp;
    1096                 :         324 :   pp_printf (&pp, "%u", arg_idx + 1 - method_p (fndecl));
    1097                 :         324 :   return label_text::take (xstrdup (pp_formatted_text (&pp)));
    1098                 :         324 : }
    1099                 :             : 
    1100                 :             : /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
    1101                 :             :    Issue a note informing that the pertinent argument must be non-NULL.  */
    1102                 :             : 
    1103                 :             : static void
    1104                 :         114 : inform_nonnull_attribute (tree fndecl, int arg_idx)
    1105                 :             : {
    1106                 :         114 :   label_text arg_desc = describe_argument_index (fndecl, arg_idx);
    1107                 :         114 :   inform (DECL_SOURCE_LOCATION (fndecl),
    1108                 :             :           "argument %s of %qD must be non-null",
    1109                 :             :           arg_desc.get (), fndecl);
    1110                 :             :   /* Ideally we would use the location of the parm and underline the
    1111                 :             :      attribute also - but we don't have the location_t values at this point
    1112                 :             :      in the middle-end.
    1113                 :             :      For reference, the C and C++ FEs have get_fndecl_argument_location.  */
    1114                 :         114 : }
    1115                 :             : 
    1116                 :             : /* Concrete subclass for describing passing a possibly-NULL value to a
    1117                 :             :    function marked with __attribute__((nonnull)).  */
    1118                 :             : 
    1119                 :           0 : class possible_null_arg : public possible_null
    1120                 :             : {
    1121                 :             : public:
    1122                 :          73 :   possible_null_arg (const malloc_state_machine &sm, tree arg,
    1123                 :             :                      tree fndecl, int arg_idx)
    1124                 :          73 :   : possible_null (sm, arg),
    1125                 :          73 :     m_fndecl (fndecl), m_arg_idx (arg_idx)
    1126                 :             :   {}
    1127                 :             : 
    1128                 :        1058 :   const char *get_kind () const final override { return "possible_null_arg"; }
    1129                 :             : 
    1130                 :          73 :   bool subclass_equal_p (const pending_diagnostic &base_other)
    1131                 :             :     const final override
    1132                 :             :   {
    1133                 :          73 :     const possible_null_arg &sub_other
    1134                 :             :       = (const possible_null_arg &)base_other;
    1135                 :          73 :     return (same_tree_p (m_arg, sub_other.m_arg)
    1136                 :          73 :             && m_fndecl == sub_other.m_fndecl
    1137                 :         146 :             && m_arg_idx == sub_other.m_arg_idx);
    1138                 :             :   }
    1139                 :             : 
    1140                 :         115 :   int get_controlling_option () const final override
    1141                 :             :   {
    1142                 :         115 :     return OPT_Wanalyzer_possible_null_argument;
    1143                 :             :   }
    1144                 :             : 
    1145                 :          42 :   bool emit (diagnostic_emission_context &ctxt) final override
    1146                 :             :   {
    1147                 :             :     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
    1148                 :          42 :     auto_diagnostic_group d;
    1149                 :          42 :     ctxt.add_cwe (690);
    1150                 :          42 :     bool warned
    1151                 :          42 :       = ctxt.warn ("use of possibly-NULL %qE where non-null expected",
    1152                 :             :                    m_arg);
    1153                 :          42 :     if (warned)
    1154                 :          42 :       inform_nonnull_attribute (m_fndecl, m_arg_idx);
    1155                 :          84 :     return warned;
    1156                 :          42 :   }
    1157                 :             : 
    1158                 :             :   bool
    1159                 :          84 :   describe_final_event (pretty_printer &pp,
    1160                 :             :                         const evdesc::final_event &ev) final override
    1161                 :             :   {
    1162                 :          84 :     label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
    1163                 :          84 :     if (m_origin_of_unchecked_event.known_p ())
    1164                 :          84 :       pp_printf (&pp,"argument %s (%qE) from %@ could be NULL"
    1165                 :             :                  " where non-null expected",
    1166                 :          84 :                  arg_desc.get (), ev.m_expr,
    1167                 :             :                  &m_origin_of_unchecked_event);
    1168                 :             :     else
    1169                 :           0 :       pp_printf (&pp,
    1170                 :             :                  "argument %s (%qE) could be NULL"
    1171                 :             :                  " where non-null expected",
    1172                 :           0 :                  arg_desc.get (), ev.m_expr);
    1173                 :          84 :     return true;
    1174                 :          84 :   }
    1175                 :             : 
    1176                 :             : private:
    1177                 :             :   tree m_fndecl;
    1178                 :             :   int m_arg_idx;
    1179                 :             : };
    1180                 :             : 
    1181                 :             : /* Concrete subclass for describing a dereference of a NULL value.  */
    1182                 :             : 
    1183                 :           0 : class null_deref : public malloc_diagnostic
    1184                 :             : {
    1185                 :             : public:
    1186                 :         332 :   null_deref (const malloc_state_machine &sm, tree arg)
    1187                 :         332 :   : malloc_diagnostic (sm, arg) {}
    1188                 :             : 
    1189                 :        1156 :   const char *get_kind () const final override { return "null_deref"; }
    1190                 :             : 
    1191                 :         277 :   int get_controlling_option () const final override
    1192                 :             :   {
    1193                 :         277 :     return OPT_Wanalyzer_null_dereference;
    1194                 :             :   }
    1195                 :             : 
    1196                 :         156 :   bool terminate_path_p () const final override { return true; }
    1197                 :             : 
    1198                 :         121 :   bool emit (diagnostic_emission_context &ctxt) final override
    1199                 :             :   {
    1200                 :             :     /* CWE-476: NULL Pointer Dereference.  */
    1201                 :         121 :     ctxt.add_cwe (476);
    1202                 :         121 :     return ctxt.warn ("dereference of NULL %qE", m_arg);
    1203                 :             :   }
    1204                 :             : 
    1205                 :             :   bool
    1206                 :          22 :   describe_return_of_state (pretty_printer &pp,
    1207                 :             :                             const evdesc::return_of_state &info)
    1208                 :             :     final override
    1209                 :             :   {
    1210                 :          22 :     if (info.m_state == m_sm.m_null)
    1211                 :             :       {
    1212                 :          16 :         pp_printf (&pp, "return of NULL to %qE from %qE",
    1213                 :          16 :                    info.m_caller_fndecl, info.m_callee_fndecl);
    1214                 :          16 :         return true;
    1215                 :             :       }
    1216                 :             :     return false;
    1217                 :             :   }
    1218                 :             : 
    1219                 :             :   bool
    1220                 :         242 :   describe_final_event (pretty_printer &pp,
    1221                 :             :                         const evdesc::final_event &ev) final override
    1222                 :             :   {
    1223                 :         242 :     pp_printf (&pp, "dereference of NULL %qE", ev.m_expr);
    1224                 :         242 :     return true;
    1225                 :             :   }
    1226                 :             : 
    1227                 :             :   /* Implementation of pending_diagnostic::supercedes_p for
    1228                 :             :      null-deref.
    1229                 :             : 
    1230                 :             :      We want null-deref to supercede use-of-unitialized-value,
    1231                 :             :      so that if we have these at the same stmt, we don't emit
    1232                 :             :      a use-of-uninitialized, just the null-deref.  */
    1233                 :             : 
    1234                 :         138 :   bool supercedes_p (const pending_diagnostic &other) const final override
    1235                 :             :   {
    1236                 :         138 :     if (other.use_of_uninit_p ())
    1237                 :             :       return true;
    1238                 :             : 
    1239                 :             :     return false;
    1240                 :             :   }
    1241                 :             : };
    1242                 :             : 
    1243                 :             : /* Concrete subclass for describing passing a NULL value to a
    1244                 :             :    function marked with __attribute__((nonnull)).  */
    1245                 :             : 
    1246                 :           0 : class null_arg : public malloc_diagnostic
    1247                 :             : {
    1248                 :             : public:
    1249                 :          73 :   null_arg (const malloc_state_machine &sm, tree arg,
    1250                 :             :             tree fndecl, int arg_idx)
    1251                 :          73 :   : malloc_diagnostic (sm, arg),
    1252                 :          73 :     m_fndecl (fndecl), m_arg_idx (arg_idx)
    1253                 :             :   {}
    1254                 :             : 
    1255                 :         760 :   const char *get_kind () const final override { return "null_arg"; }
    1256                 :             : 
    1257                 :          72 :   bool subclass_equal_p (const pending_diagnostic &base_other)
    1258                 :             :     const final override
    1259                 :             :   {
    1260                 :          72 :     const null_arg &sub_other
    1261                 :             :       = (const null_arg &)base_other;
    1262                 :          72 :     return (same_tree_p (m_arg, sub_other.m_arg)
    1263                 :          72 :             && m_fndecl == sub_other.m_fndecl
    1264                 :         144 :             && m_arg_idx == sub_other.m_arg_idx);
    1265                 :             :   }
    1266                 :             : 
    1267                 :         145 :   int get_controlling_option () const final override
    1268                 :             :   {
    1269                 :         145 :     return OPT_Wanalyzer_null_argument;
    1270                 :             :   }
    1271                 :             : 
    1272                 :          73 :   bool terminate_path_p () const final override { return true; }
    1273                 :             : 
    1274                 :          72 :   bool emit (diagnostic_emission_context &ctxt) final override
    1275                 :             :   {
    1276                 :             :     /* CWE-476: NULL Pointer Dereference.  */
    1277                 :          72 :     auto_diagnostic_group d;
    1278                 :          72 :     ctxt.add_cwe (476);
    1279                 :             : 
    1280                 :          72 :     bool warned;
    1281                 :          72 :     if (zerop (m_arg))
    1282                 :          67 :       warned = ctxt.warn ("use of NULL where non-null expected");
    1283                 :             :     else
    1284                 :           5 :       warned = ctxt.warn ("use of NULL %qE where non-null expected",
    1285                 :             :                           m_arg);
    1286                 :          72 :     if (warned)
    1287                 :          72 :       inform_nonnull_attribute (m_fndecl, m_arg_idx);
    1288                 :         144 :     return warned;
    1289                 :          72 :   }
    1290                 :             : 
    1291                 :             :   bool
    1292                 :         144 :   describe_final_event (pretty_printer &pp,
    1293                 :             :                         const evdesc::final_event &ev) final override
    1294                 :             :   {
    1295                 :         144 :     label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
    1296                 :         144 :     if (zerop (ev.m_expr))
    1297                 :         112 :       pp_printf (&pp,
    1298                 :             :                  "argument %s NULL where non-null expected",
    1299                 :             :                  arg_desc.get ());
    1300                 :             :     else
    1301                 :          32 :       pp_printf (&pp,
    1302                 :             :                  "argument %s (%qE) NULL"
    1303                 :             :                  " where non-null expected",
    1304                 :          32 :                  arg_desc.get (), ev.m_expr);
    1305                 :         144 :     return true;
    1306                 :         144 :   }
    1307                 :             : 
    1308                 :             : private:
    1309                 :             :   tree m_fndecl;
    1310                 :             :   int m_arg_idx;
    1311                 :             : };
    1312                 :             : 
    1313                 :           0 : class use_after_free : public malloc_diagnostic
    1314                 :             : {
    1315                 :             : public:
    1316                 :         174 :   use_after_free (const malloc_state_machine &sm, tree arg,
    1317                 :             :                   const deallocator *deallocator)
    1318                 :         174 :   : malloc_diagnostic (sm, arg),
    1319                 :         174 :     m_deallocator (deallocator)
    1320                 :             :   {
    1321                 :         174 :     gcc_assert (deallocator);
    1322                 :         174 :   }
    1323                 :             : 
    1324                 :        1305 :   const char *get_kind () const final override { return "use_after_free"; }
    1325                 :             : 
    1326                 :         174 :   int get_controlling_option () const final override
    1327                 :             :   {
    1328                 :         174 :     return OPT_Wanalyzer_use_after_free;
    1329                 :             :   }
    1330                 :             : 
    1331                 :          77 :   bool emit (diagnostic_emission_context &ctxt) final override
    1332                 :             :   {
    1333                 :             :     /* CWE-416: Use After Free.  */
    1334                 :          77 :     ctxt.add_cwe (416);
    1335                 :          77 :     return ctxt.warn ("use after %qs of %qE",
    1336                 :          77 :                       m_deallocator->m_name, m_arg);
    1337                 :             :   }
    1338                 :             : 
    1339                 :             :   bool
    1340                 :         348 :   describe_state_change (pretty_printer &pp,
    1341                 :             :                          const evdesc::state_change &change) final override
    1342                 :             :   {
    1343                 :         348 :     if (freed_p (change.m_new_state))
    1344                 :             :       {
    1345                 :         156 :         m_free_event = change.m_event_id;
    1346                 :         156 :         switch (m_deallocator->m_wording)
    1347                 :             :           {
    1348                 :           0 :           default:
    1349                 :           0 :           case WORDING_REALLOCATED:
    1350                 :           0 :             gcc_unreachable ();
    1351                 :         124 :           case WORDING_FREED:
    1352                 :         124 :             pp_string (&pp, "freed here");
    1353                 :         124 :             return true;
    1354                 :          30 :           case WORDING_DELETED:
    1355                 :          30 :             pp_string (&pp, "deleted here");
    1356                 :          30 :             return true;
    1357                 :           2 :           case WORDING_DEALLOCATED:
    1358                 :           2 :             pp_string (&pp, "deallocated here");
    1359                 :           2 :             return true;
    1360                 :             :           }
    1361                 :             :       }
    1362                 :         192 :     return malloc_diagnostic::describe_state_change (pp, change);
    1363                 :             :   }
    1364                 :             : 
    1365                 :             :   bool
    1366                 :         156 :   describe_final_event (pretty_printer &pp,
    1367                 :             :                         const evdesc::final_event &ev) final override
    1368                 :             :   {
    1369                 :         156 :     const char *funcname = m_deallocator->m_name;
    1370                 :         156 :     if (m_free_event.known_p ())
    1371                 :         156 :       switch (m_deallocator->m_wording)
    1372                 :             :         {
    1373                 :           0 :         default:
    1374                 :           0 :         case WORDING_REALLOCATED:
    1375                 :           0 :           gcc_unreachable ();
    1376                 :         124 :         case WORDING_FREED:
    1377                 :         124 :           pp_printf (&pp,
    1378                 :             :                      "use after %qs of %qE; freed at %@",
    1379                 :         124 :                      funcname, ev.m_expr, &m_free_event);
    1380                 :         124 :           return true;
    1381                 :          30 :         case WORDING_DELETED:
    1382                 :          30 :           pp_printf (&pp,
    1383                 :             :                      "use after %qs of %qE; deleted at %@",
    1384                 :          30 :                      funcname, ev.m_expr, &m_free_event);
    1385                 :          30 :           return true;
    1386                 :           2 :         case WORDING_DEALLOCATED:
    1387                 :           2 :           pp_printf (&pp,
    1388                 :             :                      "use after %qs of %qE;"
    1389                 :             :                      " deallocated at %@",
    1390                 :           2 :                      funcname, ev.m_expr, &m_free_event);
    1391                 :           2 :           return true;
    1392                 :             :         }
    1393                 :             :     else
    1394                 :             :       {
    1395                 :           0 :         pp_printf (&pp,
    1396                 :             :                    "use after %qs of %qE",
    1397                 :           0 :                    funcname, ev.m_expr);
    1398                 :           0 :         return true;
    1399                 :             :       }
    1400                 :             :   }
    1401                 :             : 
    1402                 :             :   /* Implementation of pending_diagnostic::supercedes_p for
    1403                 :             :      use_after_free.
    1404                 :             : 
    1405                 :             :      We want use-after-free to supercede use-of-unitialized-value,
    1406                 :             :      so that if we have these at the same stmt, we don't emit
    1407                 :             :      a use-of-uninitialized, just the use-after-free.
    1408                 :             :      (this is because we fully purge information about freed
    1409                 :             :      buffers when we free them to avoid state explosions, so
    1410                 :             :      that if they are accessed after the free, it looks like
    1411                 :             :      they are uninitialized).  */
    1412                 :             : 
    1413                 :         129 :   bool supercedes_p (const pending_diagnostic &other) const final override
    1414                 :             :   {
    1415                 :         129 :     if (other.use_of_uninit_p ())
    1416                 :             :       return true;
    1417                 :             : 
    1418                 :             :     return false;
    1419                 :             :   }
    1420                 :             : 
    1421                 :             : private:
    1422                 :             :   diagnostic_event_id_t m_free_event;
    1423                 :             :   const deallocator *m_deallocator;
    1424                 :             : };
    1425                 :             : 
    1426                 :             : class malloc_leak : public malloc_diagnostic
    1427                 :             : {
    1428                 :             : public:
    1429                 :        1009 :   malloc_leak (const malloc_state_machine &sm, tree arg,
    1430                 :             :                const program_state *final_state)
    1431                 :        1009 :   : malloc_diagnostic (sm, arg),
    1432                 :        1009 :     m_final_state ()
    1433                 :             :   {
    1434                 :        1009 :     if (final_state)
    1435                 :        1009 :       m_final_state = std::make_unique<program_state> (*final_state);
    1436                 :        1009 :   }
    1437                 :             : 
    1438                 :        9470 :   const char *get_kind () const final override { return "malloc_leak"; }
    1439                 :             : 
    1440                 :         843 :   int get_controlling_option () const final override
    1441                 :             :   {
    1442                 :         843 :     return OPT_Wanalyzer_malloc_leak;
    1443                 :             :   }
    1444                 :             : 
    1445                 :         408 :   bool emit (diagnostic_emission_context &ctxt) final override
    1446                 :             :   {
    1447                 :             :     /* "CWE-401: Missing Release of Memory after Effective Lifetime".  */
    1448                 :         408 :     ctxt.add_cwe (401);
    1449                 :         408 :     if (m_arg)
    1450                 :         346 :       return ctxt.warn ("leak of %qE", m_arg);
    1451                 :             :     else
    1452                 :          62 :       return ctxt.warn ("leak of %qs", "<unknown>");
    1453                 :             :   }
    1454                 :             : 
    1455                 :             :   bool
    1456                 :        1046 :   describe_state_change (pretty_printer &pp,
    1457                 :             :                          const evdesc::state_change &change) final override
    1458                 :             :   {
    1459                 :        1046 :     if (unchecked_p (change.m_new_state)
    1460                 :        1046 :         || (start_p (change.m_old_state) && nonnull_p (change.m_new_state)))
    1461                 :             :       {
    1462                 :         786 :         m_alloc_event = change.m_event_id;
    1463                 :         786 :         pp_string (&pp, "allocated here");
    1464                 :         786 :         return true;
    1465                 :             :       }
    1466                 :         260 :     return malloc_diagnostic::describe_state_change (pp, change);
    1467                 :             :   }
    1468                 :             : 
    1469                 :             :   bool
    1470                 :         804 :   describe_final_event (pretty_printer &pp,
    1471                 :             :                         const evdesc::final_event &ev) final override
    1472                 :             :   {
    1473                 :         804 :     if (ev.m_expr)
    1474                 :             :       {
    1475                 :         680 :         if (m_alloc_event.known_p ())
    1476                 :         680 :           pp_printf (&pp,
    1477                 :             :                      "%qE leaks here; was allocated at %@",
    1478                 :             :                      ev.m_expr, &m_alloc_event);
    1479                 :             :         else
    1480                 :           0 :           pp_printf (&pp,
    1481                 :             :                      "%qE leaks here", ev.m_expr);
    1482                 :             :       }
    1483                 :             :     else
    1484                 :             :       {
    1485                 :         124 :         if (m_alloc_event.known_p ())
    1486                 :         106 :           pp_printf (&pp,
    1487                 :             :                      "%qs leaks here; was allocated at %@",
    1488                 :             :                      "<unknown>", &m_alloc_event);
    1489                 :             :         else
    1490                 :          18 :           pp_printf (&pp,
    1491                 :             :                      "%qs leaks here", "<unknown>");
    1492                 :             :       }
    1493                 :         804 :     return true;
    1494                 :             :   }
    1495                 :             : 
    1496                 :             :   const program_state *
    1497                 :         408 :   get_final_state () const final override
    1498                 :             :   {
    1499                 :         408 :     return m_final_state.get ();
    1500                 :             :   }
    1501                 :             : 
    1502                 :             : private:
    1503                 :             :   diagnostic_event_id_t m_alloc_event;
    1504                 :             :   std::unique_ptr<program_state> m_final_state;
    1505                 :             : };
    1506                 :             : 
    1507                 :           0 : class free_of_non_heap : public malloc_diagnostic
    1508                 :             : {
    1509                 :             : public:
    1510                 :          53 :   free_of_non_heap (const malloc_state_machine &sm, tree arg,
    1511                 :             :                     const region *freed_reg,
    1512                 :             :                     const char *funcname)
    1513                 :          53 :   : malloc_diagnostic (sm, arg), m_freed_reg (freed_reg), m_funcname (funcname)
    1514                 :             :   {
    1515                 :             :   }
    1516                 :             : 
    1517                 :         595 :   const char *get_kind () const final override { return "free_of_non_heap"; }
    1518                 :             : 
    1519                 :          45 :   bool subclass_equal_p (const pending_diagnostic &base_other) const
    1520                 :             :     final override
    1521                 :             :   {
    1522                 :          45 :     const free_of_non_heap &other = (const free_of_non_heap &)base_other;
    1523                 :          45 :     return (same_tree_p (m_arg, other.m_arg)
    1524                 :          45 :             && m_freed_reg == other.m_freed_reg);
    1525                 :             :   }
    1526                 :             : 
    1527                 :          94 :   int get_controlling_option () const final override
    1528                 :             :   {
    1529                 :          94 :     return OPT_Wanalyzer_free_of_non_heap;
    1530                 :             :   }
    1531                 :             : 
    1532                 :          41 :   bool emit (diagnostic_emission_context &ctxt) final override
    1533                 :             :   {
    1534                 :          41 :     auto_diagnostic_group d;
    1535                 :          41 :     ctxt.add_cwe (590); /* CWE-590: Free of Memory not on the Heap.  */
    1536                 :          41 :     switch (get_memory_space ())
    1537                 :             :       {
    1538                 :           0 :       default:
    1539                 :           0 :       case MEMSPACE_HEAP:
    1540                 :           0 :         gcc_unreachable ();
    1541                 :          10 :       case MEMSPACE_UNKNOWN:
    1542                 :          10 :       case MEMSPACE_CODE:
    1543                 :          10 :       case MEMSPACE_GLOBALS:
    1544                 :          10 :       case MEMSPACE_READONLY_DATA:
    1545                 :          10 :         return ctxt.warn ("%qs of %qE which points to memory"
    1546                 :             :                           " not on the heap",
    1547                 :          10 :                           m_funcname, m_arg);
    1548                 :          31 :         break;
    1549                 :          31 :       case MEMSPACE_STACK:
    1550                 :          31 :         return ctxt.warn ("%qs of %qE which points to memory"
    1551                 :             :                           " on the stack",
    1552                 :          31 :                           m_funcname, m_arg);
    1553                 :          41 :         break;
    1554                 :             :       }
    1555                 :          41 :   }
    1556                 :             : 
    1557                 :             :   bool
    1558                 :           0 :   describe_state_change (pretty_printer &pp,
    1559                 :             :                          const evdesc::state_change &) final override
    1560                 :             :   {
    1561                 :           0 :     pp_string (&pp, "pointer is from here");
    1562                 :           0 :     return true;
    1563                 :             :   }
    1564                 :             : 
    1565                 :             :   bool
    1566                 :          82 :   describe_final_event (pretty_printer &pp,
    1567                 :             :                         const evdesc::final_event &) final override
    1568                 :             :   {
    1569                 :          82 :     pp_printf (&pp, "call to %qs here", m_funcname);
    1570                 :          82 :     return true;
    1571                 :             :   }
    1572                 :             : 
    1573                 :          41 :   void mark_interesting_stuff (interesting_t *interest) final override
    1574                 :             :   {
    1575                 :          41 :     if (m_freed_reg)
    1576                 :          41 :       interest->add_region_creation (m_freed_reg);
    1577                 :          41 :   }
    1578                 :             : 
    1579                 :             : private:
    1580                 :          41 :   enum memory_space get_memory_space () const
    1581                 :             :   {
    1582                 :          41 :     if (m_freed_reg)
    1583                 :          41 :       return m_freed_reg->get_memory_space ();
    1584                 :             :     else
    1585                 :             :       return MEMSPACE_UNKNOWN;
    1586                 :             :   }
    1587                 :             : 
    1588                 :             :   const region *m_freed_reg;
    1589                 :             :   const char *m_funcname;
    1590                 :             : };
    1591                 :             : 
    1592                 :             : /* Concrete pending_diagnostic subclass for -Wanalyzer-deref-before-check.  */
    1593                 :             : 
    1594                 :           0 : class deref_before_check : public malloc_diagnostic
    1595                 :             : {
    1596                 :             : public:
    1597                 :         155 :   deref_before_check (const malloc_state_machine &sm, tree arg)
    1598                 :         155 :   : malloc_diagnostic (sm, arg),
    1599                 :         155 :     m_deref_enode (nullptr),
    1600                 :         155 :     m_deref_expr (nullptr),
    1601                 :         155 :     m_check_enode (nullptr)
    1602                 :             :   {
    1603                 :         155 :     gcc_assert (arg);
    1604                 :         155 :   }
    1605                 :             : 
    1606                 :        1584 :   const char *get_kind () const final override { return "deref_before_check"; }
    1607                 :             : 
    1608                 :         209 :   int get_controlling_option () const final override
    1609                 :             :   {
    1610                 :         209 :     return OPT_Wanalyzer_deref_before_check;
    1611                 :             :   }
    1612                 :             : 
    1613                 :         110 :   bool emit (diagnostic_emission_context &ctxt) final override
    1614                 :             :   {
    1615                 :             :     /* Don't emit the warning if we can't show where the deref
    1616                 :             :        and the check occur.  */
    1617                 :         110 :     if (!m_deref_enode)
    1618                 :             :       return false;
    1619                 :         110 :     if (!m_check_enode)
    1620                 :             :       return false;
    1621                 :             :     /* Only emit the warning for intraprocedural cases.  */
    1622                 :         110 :     const program_point &deref_point = m_deref_enode->get_point ();
    1623                 :         110 :     const program_point &check_point = m_check_enode->get_point ();
    1624                 :             : 
    1625                 :         110 :     if (!program_point::effectively_intraprocedural_p (deref_point,
    1626                 :             :                                                        check_point))
    1627                 :             :       return false;
    1628                 :             : 
    1629                 :             :     /* Reject the warning if the check occurs within a macro defintion.
    1630                 :             :        This avoids false positives for such code as:
    1631                 :             : 
    1632                 :             :         #define throw_error \
    1633                 :             :            do {             \
    1634                 :             :              if (p)         \
    1635                 :             :                cleanup (p); \
    1636                 :             :              return;        \
    1637                 :             :            } while (0)
    1638                 :             : 
    1639                 :             :         if (p->idx >= n)
    1640                 :             :           throw_error ();
    1641                 :             : 
    1642                 :             :        where the usage of "throw_error" implicitly adds a check
    1643                 :             :        on 'p'.
    1644                 :             : 
    1645                 :             :        We do warn when the check is in a macro expansion if we can get
    1646                 :             :        at the location of the condition and it is't part of the
    1647                 :             :        definition, so that we warn for checks such as:
    1648                 :             :            if (words[0][0] == '@')
    1649                 :             :              return;
    1650                 :             :            g_assert(words[0] != NULL); <--- here
    1651                 :             :        Unfortunately we don't have locations for individual gimple
    1652                 :             :        arguments, so in:
    1653                 :             :            g_assert (ptr);
    1654                 :             :        we merely have a gimple_cond
    1655                 :             :            if (p_2(D) == 0B)
    1656                 :             :        with no way of getting at the location of the condition separately
    1657                 :             :        from that of the gimple_cond (where the "if" is within the macro
    1658                 :             :        definition).  We reject the warning for such cases.
    1659                 :             : 
    1660                 :             :        We do warn when the *deref* occurs in a macro, since this can be
    1661                 :             :        a source of real bugs; see e.g. PR 77425.  */
    1662                 :          94 :     location_t check_loc = m_check_enode->get_point ().get_location ();
    1663                 :          94 :     if (linemap_location_from_macro_definition_p (line_table, check_loc))
    1664                 :             :       return false;
    1665                 :             : 
    1666                 :             :     /* Reject warning if the check is in a loop header within a
    1667                 :             :        macro expansion.  This rejects cases like:
    1668                 :             :        |  deref of x;
    1669                 :             :        |  [...snip...]
    1670                 :             :        |  FOR_EACH(x) {
    1671                 :             :        |    [...snip...]
    1672                 :             :        |  }
    1673                 :             :        where the FOR_EACH macro tests for non-nullness of x, since
    1674                 :             :        the user is hoping to encapsulate the details of iteration
    1675                 :             :        in the macro, and the extra check on the first iteration
    1676                 :             :        would just be noise if we reported it.  */
    1677                 :          88 :     if (loop_header_p (m_check_enode->get_point ())
    1678                 :          88 :         && linemap_location_from_macro_expansion_p (line_table, check_loc))
    1679                 :             :       return false;
    1680                 :             : 
    1681                 :             :     /* Reject if m_deref_expr is sufficiently different from m_arg
    1682                 :             :        for cases where the dereference is spelled differently from
    1683                 :             :        the check, which is probably two different ways to get the
    1684                 :             :        same svalue, and thus not worth reporting.  */
    1685                 :          84 :     if (!m_deref_expr)
    1686                 :             :       return false;
    1687                 :          66 :     if (!sufficiently_similar_p (m_deref_expr, m_arg))
    1688                 :             :       return false;
    1689                 :             : 
    1690                 :             :     /* Reject the warning if the deref's BB doesn't dominate that
    1691                 :             :        of the check, so that we don't warn e.g. for shared cleanup
    1692                 :             :        code that checks a pointer for NULL, when that code is sometimes
    1693                 :             :        used before a deref and sometimes after.
    1694                 :             :        Using the dominance code requires setting cfun.  */
    1695                 :          62 :     auto_cfun sentinel (m_deref_enode->get_function ());
    1696                 :          62 :     calculate_dominance_info (CDI_DOMINATORS);
    1697                 :          62 :     if (!dominated_by_p (CDI_DOMINATORS,
    1698                 :          62 :                          m_check_enode->get_supernode ()->m_bb,
    1699                 :          62 :                          m_deref_enode->get_supernode ()->m_bb))
    1700                 :             :       return false;
    1701                 :             : 
    1702                 :          54 :     return ctxt.warn ("check of %qE for NULL after already"
    1703                 :             :                       " dereferencing it",
    1704                 :          54 :                       m_arg);
    1705                 :             :   }
    1706                 :             : 
    1707                 :             :   bool
    1708                 :         164 :   describe_state_change (pretty_printer &pp,
    1709                 :             :                          const evdesc::state_change &change) final override
    1710                 :             :   {
    1711                 :         164 :     if (change.m_old_state == m_sm.get_start_state ()
    1712                 :         164 :         && assumed_non_null_p (change.m_new_state))
    1713                 :             :       {
    1714                 :         164 :         m_first_deref_event = change.m_event_id;
    1715                 :         164 :         m_deref_enode = change.m_event.get_exploded_node ();
    1716                 :         164 :         m_deref_expr = change.m_expr;
    1717                 :         164 :         pp_printf (&pp,
    1718                 :             :                    "pointer %qE is dereferenced here",
    1719                 :             :                    m_arg);
    1720                 :         164 :         return true;
    1721                 :             :       }
    1722                 :           0 :     return malloc_diagnostic::describe_state_change (pp, change);
    1723                 :             :   }
    1724                 :             : 
    1725                 :             :   bool
    1726                 :         164 :   describe_final_event (pretty_printer &pp,
    1727                 :             :                         const evdesc::final_event &ev) final override
    1728                 :             :   {
    1729                 :         164 :     m_check_enode = ev.m_event.get_exploded_node ();
    1730                 :         164 :     if (m_first_deref_event.known_p ())
    1731                 :         164 :       pp_printf (&pp,
    1732                 :             :                  "pointer %qE is checked for NULL here but"
    1733                 :             :                  " it was already dereferenced at %@",
    1734                 :             :                  m_arg, &m_first_deref_event);
    1735                 :             :     else
    1736                 :           0 :       pp_printf (&pp,
    1737                 :             :                  "pointer %qE is checked for NULL here but"
    1738                 :             :                  " it was already dereferenced",
    1739                 :             :                  m_arg);
    1740                 :         164 :     return true;
    1741                 :             :   }
    1742                 :             : 
    1743                 :             : private:
    1744                 :          88 :   static bool loop_header_p (const program_point &point)
    1745                 :             :   {
    1746                 :          88 :     const supernode *snode = point.get_supernode ();
    1747                 :          88 :     if (!snode)
    1748                 :             :       return false;
    1749                 :         382 :     for (auto &in_edge : snode->m_preds)
    1750                 :             :       {
    1751                 :         244 :         if (const cfg_superedge *cfg_in_edge
    1752                 :         122 :               = in_edge->dyn_cast_cfg_superedge ())
    1753                 :         120 :           if (cfg_in_edge->back_edge_p ())
    1754                 :             :             return true;
    1755                 :             :       }
    1756                 :             :     return false;
    1757                 :             :   }
    1758                 :             : 
    1759                 :          66 :   static bool sufficiently_similar_p (tree expr_a, tree expr_b)
    1760                 :             :   {
    1761                 :          66 :     auto pp_a = global_dc->clone_printer ();
    1762                 :          66 :     auto pp_b = global_dc->clone_printer ();
    1763                 :          66 :     pp_printf (pp_a.get (), "%qE", expr_a);
    1764                 :          66 :     pp_printf (pp_b.get (), "%qE", expr_b);
    1765                 :          66 :     bool result = (strcmp (pp_formatted_text (pp_a.get ()),
    1766                 :             :                            pp_formatted_text (pp_b.get ()))
    1767                 :          66 :                    == 0);
    1768                 :         132 :     return result;
    1769                 :          66 :   }
    1770                 :             : 
    1771                 :             :   diagnostic_event_id_t m_first_deref_event;
    1772                 :             :   const exploded_node *m_deref_enode;
    1773                 :             :   tree m_deref_expr;
    1774                 :             :   const exploded_node *m_check_enode;
    1775                 :             : };
    1776                 :             : 
    1777                 :             : /* struct allocation_state : public state_machine::state.  */
    1778                 :             : 
    1779                 :             : /* Implementation of state_machine::state::dump_to_pp vfunc
    1780                 :             :    for allocation_state: append the API that this allocation is
    1781                 :             :    associated with.  */
    1782                 :             : 
    1783                 :             : void
    1784                 :         807 : allocation_state::dump_to_pp (pretty_printer *pp) const
    1785                 :             : {
    1786                 :         807 :   state_machine::state::dump_to_pp (pp);
    1787                 :         807 :   if (m_deallocators)
    1788                 :             :     {
    1789                 :         420 :       pp_string (pp, " (");
    1790                 :         420 :       m_deallocators->dump_to_pp (pp);
    1791                 :         420 :       pp_character (pp, ')');
    1792                 :             :     }
    1793                 :         807 : }
    1794                 :             : 
    1795                 :             : /* Given a allocation_state for a deallocator_set, get the "nonnull" state
    1796                 :             :    for the corresponding allocator(s).  */
    1797                 :             : 
    1798                 :             : const allocation_state *
    1799                 :        2236 : allocation_state::get_nonnull () const
    1800                 :             : {
    1801                 :        2236 :   gcc_assert (m_deallocators);
    1802                 :        2236 :   return as_a_allocation_state (m_deallocators->m_nonnull);
    1803                 :             : }
    1804                 :             : 
    1805                 :             : /* struct assumed_non_null_state : public allocation_state.  */
    1806                 :             : 
    1807                 :             : void
    1808                 :         377 : assumed_non_null_state::dump_to_pp (pretty_printer *pp) const
    1809                 :             : {
    1810                 :         377 :   allocation_state::dump_to_pp (pp);
    1811                 :         377 :   pp_string (pp, " (in ");
    1812                 :         377 :   m_frame->dump_to_pp (pp, true);
    1813                 :         377 :   pp_character (pp, ')');
    1814                 :         377 : }
    1815                 :             : 
    1816                 :             : /* malloc_state_machine's ctor.  */
    1817                 :             : 
    1818                 :        3320 : malloc_state_machine::malloc_state_machine (logger *logger)
    1819                 :             : : state_machine ("malloc", logger),
    1820                 :        3320 :   m_free (this, "free", WORDING_FREED),
    1821                 :        3320 :   m_scalar_delete (this, "delete", WORDING_DELETED),
    1822                 :        3320 :   m_vector_delete (this, "delete[]", WORDING_DELETED),
    1823                 :        6640 :   m_realloc (this, "realloc", WORDING_REALLOCATED)
    1824                 :             : {
    1825                 :        3320 :   gcc_assert (m_start->get_id () == 0);
    1826                 :        3320 :   m_null = add_state ("null", RS_FREED, nullptr, nullptr);
    1827                 :        3320 :   m_non_heap = add_state ("non-heap", RS_NON_HEAP, nullptr, nullptr);
    1828                 :        3320 :   m_stop = add_state ("stop", RS_STOP, nullptr, nullptr);
    1829                 :        3320 : }
    1830                 :             : 
    1831                 :        6640 : malloc_state_machine::~malloc_state_machine ()
    1832                 :             : {
    1833                 :        3320 :   unsigned i;
    1834                 :        3320 :   custom_deallocator_set *set;
    1835                 :        3395 :   FOR_EACH_VEC_ELT (m_dynamic_sets, i, set)
    1836                 :          75 :     delete set;
    1837                 :             :   custom_deallocator *d;
    1838                 :        3390 :   FOR_EACH_VEC_ELT (m_dynamic_deallocators, i, d)
    1839                 :          70 :     delete d;
    1840                 :        6640 : }
    1841                 :             : 
    1842                 :             : state_machine::state_t
    1843                 :       43380 : malloc_state_machine::add_state (const char *name, enum resource_state rs,
    1844                 :             :                                  const deallocator_set *deallocators,
    1845                 :             :                                  const deallocator *deallocator)
    1846                 :             : {
    1847                 :       86760 :   return add_custom_state (new allocation_state (name, alloc_state_id (),
    1848                 :             :                                                  rs, deallocators,
    1849                 :       43380 :                                                  deallocator));
    1850                 :             : }
    1851                 :             : 
    1852                 :             : /* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
    1853                 :             :    return a custom_deallocator_set for them, consolidating them
    1854                 :             :    to ensure uniqueness of the sets.
    1855                 :             : 
    1856                 :             :    Return nullptr if it has no such attributes.  */
    1857                 :             : 
    1858                 :             : const custom_deallocator_set *
    1859                 :       36721 : malloc_state_machine::
    1860                 :             : get_or_create_custom_deallocator_set (tree allocator_fndecl)
    1861                 :             : {
    1862                 :             :   /* Early rejection of decls without attributes.  */
    1863                 :       36721 :   tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
    1864                 :       36721 :   if (!attrs)
    1865                 :             :     return nullptr;
    1866                 :             : 
    1867                 :             :   /* Otherwise, call maybe_create_custom_deallocator_set,
    1868                 :             :      memoizing the result.  */
    1869                 :       16812 :   if (custom_deallocator_set **slot
    1870                 :        8406 :       = m_custom_deallocator_set_cache.get (allocator_fndecl))
    1871                 :        6184 :     return *slot;
    1872                 :        2222 :   custom_deallocator_set *set
    1873                 :        2222 :     = maybe_create_custom_deallocator_set (allocator_fndecl);
    1874                 :        2222 :   m_custom_deallocator_set_cache.put (allocator_fndecl, set);
    1875                 :        2222 :   return set;
    1876                 :             : }
    1877                 :             : 
    1878                 :             : /* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
    1879                 :             :    look for any "__attribute__((malloc(FOO)))" and return a
    1880                 :             :    custom_deallocator_set for them, consolidating them
    1881                 :             :    to ensure uniqueness of the sets.
    1882                 :             : 
    1883                 :             :    Return nullptr if it has no such attributes.
    1884                 :             : 
    1885                 :             :    Subroutine of get_or_create_custom_deallocator_set which
    1886                 :             :    memoizes the result.  */
    1887                 :             : 
    1888                 :             : custom_deallocator_set *
    1889                 :        2222 : malloc_state_machine::
    1890                 :             : maybe_create_custom_deallocator_set (tree allocator_fndecl)
    1891                 :             : {
    1892                 :        2222 :   tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
    1893                 :        2222 :   gcc_assert (attrs);
    1894                 :             : 
    1895                 :             :   /* Look for instances of __attribute__((malloc(FOO))).  */
    1896                 :        2222 :   auto_vec<const deallocator *> deallocator_vec;
    1897                 :        2222 :   for (tree allocs = attrs;
    1898                 :        2444 :        (allocs = lookup_attribute ("malloc", allocs));
    1899                 :         222 :        allocs = TREE_CHAIN (allocs))
    1900                 :             :     {
    1901                 :         222 :       tree args = TREE_VALUE (allocs);
    1902                 :         222 :       if (!args)
    1903                 :         114 :         continue;
    1904                 :         108 :       if (TREE_VALUE (args))
    1905                 :             :         {
    1906                 :         108 :           const deallocator *d
    1907                 :         108 :             = get_or_create_deallocator (TREE_VALUE (args));
    1908                 :         108 :           deallocator_vec.safe_push (d);
    1909                 :             :         }
    1910                 :             :     }
    1911                 :             : 
    1912                 :             :   /* If there weren't any deallocators, bail.  */
    1913                 :        2310 :   if (deallocator_vec.length () == 0)
    1914                 :             :     return nullptr;
    1915                 :             : 
    1916                 :             :   /* Consolidate, so that we reuse existing deallocator_set
    1917                 :             :      instances.  */
    1918                 :          88 :   deallocator_vec.qsort (deallocator::cmp_ptr_ptr);
    1919                 :          88 :   custom_deallocator_set **slot
    1920                 :          88 :     = m_custom_deallocator_set_map.get (&deallocator_vec);
    1921                 :          88 :   if (slot)
    1922                 :          13 :     return *slot;
    1923                 :          75 :   custom_deallocator_set *set
    1924                 :          75 :     = new custom_deallocator_set (this, &deallocator_vec, WORDING_DEALLOCATED);
    1925                 :          75 :   m_custom_deallocator_set_map.put (&set->m_deallocator_vec, set);
    1926                 :          75 :   m_dynamic_sets.safe_push (set);
    1927                 :          75 :   return set;
    1928                 :        2222 : }
    1929                 :             : 
    1930                 :             : /* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary.  */
    1931                 :             : 
    1932                 :             : const deallocator *
    1933                 :         328 : malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl)
    1934                 :             : {
    1935                 :         328 :   deallocator **slot = m_deallocator_map.get (deallocator_fndecl);
    1936                 :         328 :   if (slot)
    1937                 :         248 :     return *slot;
    1938                 :             : 
    1939                 :             :   /* Reuse "free".  */
    1940                 :          80 :   deallocator *d;
    1941                 :          80 :   if (is_named_call_p (deallocator_fndecl, "free")
    1942                 :          71 :       || is_std_named_call_p (deallocator_fndecl, "free")
    1943                 :         151 :       || is_named_call_p (deallocator_fndecl, "__builtin_free"))
    1944                 :          10 :     d = &m_free.m_deallocator;
    1945                 :             :   else
    1946                 :             :     {
    1947                 :          70 :       custom_deallocator *cd
    1948                 :             :         = new custom_deallocator (this, deallocator_fndecl,
    1949                 :          70 :                                   WORDING_DEALLOCATED);
    1950                 :          70 :       m_dynamic_deallocators.safe_push (cd);
    1951                 :          70 :       d = cd;
    1952                 :             :     }
    1953                 :          80 :   m_deallocator_map.put (deallocator_fndecl, d);
    1954                 :          80 :   return d;
    1955                 :             : }
    1956                 :             : 
    1957                 :             : /* Get the "assumed-non-null" state for assumptions made within FRAME,
    1958                 :             :    creating it if necessary.  */
    1959                 :             : 
    1960                 :             : state_machine::state_t
    1961                 :       14192 : malloc_state_machine::
    1962                 :             : get_or_create_assumed_non_null_state_for_frame (const frame_region *frame)
    1963                 :             : {
    1964                 :       14192 :   if (state_t *slot = m_assumed_non_null.get (frame))
    1965                 :       11533 :     return *slot;
    1966                 :        2659 :   state_machine::state *new_state
    1967                 :        2659 :     = new assumed_non_null_state ("assumed-non-null", alloc_state_id (), frame);
    1968                 :        2659 :   add_custom_state (new_state);
    1969                 :        2659 :   m_assumed_non_null.put (frame, new_state);
    1970                 :        2659 :   return new_state;
    1971                 :             : }
    1972                 :             : 
    1973                 :             : /* Try to identify the function declaration either by name or as a known malloc
    1974                 :             :    builtin.  */
    1975                 :             : 
    1976                 :             : static bool
    1977                 :       56351 : known_allocator_p (const_tree fndecl, const gcall &call)
    1978                 :             : {
    1979                 :             :   /* Either it is a function we know by name and number of arguments... */
    1980                 :       56351 :   if (is_named_call_p (fndecl, "malloc", call, 1)
    1981                 :       54277 :       || is_named_call_p (fndecl, "calloc", call, 2)
    1982                 :       54160 :       || is_std_named_call_p (fndecl, "malloc", call, 1)
    1983                 :       54157 :       || is_std_named_call_p (fndecl, "calloc", call, 2)
    1984                 :       54154 :       || is_named_call_p (fndecl, "strdup", call, 1)
    1985                 :      110307 :       || is_named_call_p (fndecl, "strndup", call, 2))
    1986                 :        2409 :     return true;
    1987                 :             : 
    1988                 :             :   /* ... or it is a builtin allocator that allocates objects freed with
    1989                 :             :      __builtin_free.  */
    1990                 :       53942 :   if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
    1991                 :       22435 :     switch (DECL_FUNCTION_CODE (fndecl))
    1992                 :             :       {
    1993                 :             :       case BUILT_IN_MALLOC:
    1994                 :             :       case BUILT_IN_CALLOC:
    1995                 :             :       case BUILT_IN_STRDUP:
    1996                 :             :       case BUILT_IN_STRNDUP:
    1997                 :             :         return true;
    1998                 :             :       default:
    1999                 :             :         break;
    2000                 :             :       }
    2001                 :             : 
    2002                 :             :   return false;
    2003                 :             : }
    2004                 :             : 
    2005                 :             : /* If PTR's nullness is not known, transition it to the "assumed-non-null"
    2006                 :             :    state for the current frame.  */
    2007                 :             : 
    2008                 :             : void
    2009                 :       24263 : malloc_state_machine::maybe_assume_non_null (sm_context &sm_ctxt,
    2010                 :             :                                              tree ptr,
    2011                 :             :                                              const gimple *stmt) const
    2012                 :             : {
    2013                 :       24263 :   const region_model *old_model = sm_ctxt.get_old_region_model ();
    2014                 :       24263 :   if (!old_model)
    2015                 :           0 :     return;
    2016                 :             : 
    2017                 :       24263 :   tree null_ptr_cst = build_int_cst (TREE_TYPE (ptr), 0);
    2018                 :       24263 :   tristate known_non_null
    2019                 :       24263 :     = old_model->eval_condition (ptr, NE_EXPR, null_ptr_cst, nullptr);
    2020                 :       24263 :   if (known_non_null.is_unknown ())
    2021                 :             :     {
    2022                 :             :       /* Cast away const-ness for cache-like operations.  */
    2023                 :       14192 :       malloc_state_machine *mut_this
    2024                 :             :         = const_cast <malloc_state_machine *> (this);
    2025                 :       14192 :       state_t next_state
    2026                 :             :         = mut_this->get_or_create_assumed_non_null_state_for_frame
    2027                 :       14192 :         (old_model->get_current_frame ());
    2028                 :       14192 :       sm_ctxt.set_next_state (stmt, ptr, next_state);
    2029                 :             :     }
    2030                 :             : }
    2031                 :             : 
    2032                 :             : /* Helper method for malloc_state_machine::on_stmt.  Handle a single
    2033                 :             :    argument (Ith argument ARG) if it is nonnull or nonnull_if_nonzero
    2034                 :             :    and size is nonzero.  */
    2035                 :             : 
    2036                 :             : void
    2037                 :        7101 : malloc_state_machine::handle_nonnull (sm_context &sm_ctxt,
    2038                 :             :                                       const supernode *node,
    2039                 :             :                                       const gimple *stmt,
    2040                 :             :                                       tree fndecl,
    2041                 :             :                                       tree arg,
    2042                 :             :                                       unsigned i) const
    2043                 :             : {
    2044                 :        7101 :   state_t state = sm_ctxt.get_state (stmt, arg);
    2045                 :             :   /* Can't use a switch as the states are non-const.  */
    2046                 :             :   /* Do use the fndecl that caused the warning so that the
    2047                 :             :      misused attributes are printed and the user not confused.  */
    2048                 :        7101 :   if (unchecked_p (state))
    2049                 :             :     {
    2050                 :          73 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2051                 :          73 :       sm_ctxt.warn
    2052                 :          73 :         (node, stmt, arg,
    2053                 :          73 :          std::make_unique<possible_null_arg> (*this, diag_arg, fndecl,
    2054                 :             :                                               i));
    2055                 :          73 :       const allocation_state *astate
    2056                 :          73 :         = as_a_allocation_state (state);
    2057                 :          73 :       sm_ctxt.set_next_state (stmt, arg, astate->get_nonnull ());
    2058                 :             :     }
    2059                 :        7028 :   else if (state == m_null)
    2060                 :             :     {
    2061                 :          73 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2062                 :          73 :       sm_ctxt.warn (node, stmt, arg,
    2063                 :          73 :                     std::make_unique<null_arg> (*this, diag_arg, fndecl, i));
    2064                 :          73 :       sm_ctxt.set_next_state (stmt, arg, m_stop);
    2065                 :             :     }
    2066                 :        6955 :   else if (state == m_start)
    2067                 :        3358 :     maybe_assume_non_null (sm_ctxt, arg, stmt);
    2068                 :        7101 : }
    2069                 :             : 
    2070                 :             : /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine.  */
    2071                 :             : 
    2072                 :             : bool
    2073                 :      269782 : malloc_state_machine::on_stmt (sm_context &sm_ctxt,
    2074                 :             :                                const supernode *node,
    2075                 :             :                                const gimple *stmt) const
    2076                 :             : {
    2077                 :      269782 :   if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt))
    2078                 :       57833 :     if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call_stmt))
    2079                 :             :       {
    2080                 :       56351 :         const gcall &call = *call_stmt;
    2081                 :             : 
    2082                 :       56351 :         if (known_allocator_p (callee_fndecl, call))
    2083                 :             :           {
    2084                 :        7165 :             on_allocator_call (sm_ctxt, call, &m_free);
    2085                 :        7165 :             return true;
    2086                 :             :           }
    2087                 :             : 
    2088                 :       49186 :         if (!is_placement_new_p (call))
    2089                 :             :           {
    2090                 :       98258 :             bool returns_nonnull = !TREE_NOTHROW (callee_fndecl)
    2091                 :       49129 :                                    && flag_exceptions;
    2092                 :       49129 :             if (is_named_call_p (callee_fndecl, "operator new"))
    2093                 :         120 :               on_allocator_call (sm_ctxt, call,
    2094                 :             :                                  &m_scalar_delete, returns_nonnull);
    2095                 :       49009 :             else if (is_named_call_p (callee_fndecl, "operator new []"))
    2096                 :          53 :               on_allocator_call (sm_ctxt, call,
    2097                 :             :                                  &m_vector_delete, returns_nonnull);
    2098                 :             :           }
    2099                 :             : 
    2100                 :       49186 :         if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
    2101                 :       49186 :             || is_named_call_p (callee_fndecl, "operator delete", call, 2))
    2102                 :             :           {
    2103                 :         170 :             on_deallocator_call (sm_ctxt, node, call,
    2104                 :             :                                  &m_scalar_delete.m_deallocator, 0);
    2105                 :         170 :             return true;
    2106                 :             :           }
    2107                 :       49016 :         else if (is_named_call_p (callee_fndecl, "operator delete []", call, 1))
    2108                 :             :           {
    2109                 :          36 :             on_deallocator_call (sm_ctxt, node, call,
    2110                 :             :                                  &m_vector_delete.m_deallocator, 0);
    2111                 :          36 :             return true;
    2112                 :             :           }
    2113                 :             : 
    2114                 :       48980 :         if (is_named_call_p (callee_fndecl, "alloca", call, 1)
    2115                 :       48980 :             || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
    2116                 :             :           {
    2117                 :         193 :             tree lhs = gimple_call_lhs (&call);
    2118                 :         193 :             if (lhs)
    2119                 :         193 :               sm_ctxt.on_transition (node, stmt, lhs, m_start, m_non_heap);
    2120                 :         193 :             return true;
    2121                 :             :           }
    2122                 :             : 
    2123                 :       48787 :         if (is_named_call_p (callee_fndecl, "free", call, 1)
    2124                 :       45943 :             || is_std_named_call_p (callee_fndecl, "free", call, 1)
    2125                 :       94724 :             || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
    2126                 :             :           {
    2127                 :       11598 :             on_deallocator_call (sm_ctxt, node, call,
    2128                 :             :                                  &m_free.m_deallocator, 0);
    2129                 :       11598 :             return true;
    2130                 :             :           }
    2131                 :             : 
    2132                 :       37189 :         if (is_named_call_p (callee_fndecl, "realloc", call, 2)
    2133                 :       36751 :             || is_std_named_call_p (callee_fndecl, "realloc", call, 2)
    2134                 :       73940 :             || is_named_call_p (callee_fndecl, "__builtin_realloc", call, 2))
    2135                 :             :           {
    2136                 :         460 :             on_realloc_call (sm_ctxt, node, call);
    2137                 :         460 :             return true;
    2138                 :             :           }
    2139                 :             : 
    2140                 :       36729 :         if (unaffected_by_call_p (callee_fndecl))
    2141                 :             :           return true;
    2142                 :             : 
    2143                 :             :         /* Cast away const-ness for cache-like operations.  */
    2144                 :       36721 :         malloc_state_machine *mutable_this
    2145                 :             :           = const_cast <malloc_state_machine *> (this);
    2146                 :             : 
    2147                 :             :         /* Handle interesting attributes of the callee_fndecl,
    2148                 :             :            or prioritize those of the builtin that callee_fndecl is expected
    2149                 :             :            to be.
    2150                 :             :            Might want this to be controlled by a flag.  */
    2151                 :       36721 :         {
    2152                 :       36721 :           tree fndecl = callee_fndecl;
    2153                 :             :           /* If call is recognized as a builtin known_function, use that
    2154                 :             :              builtin's function_decl.  */
    2155                 :       36721 :           if (const region_model *old_model = sm_ctxt.get_old_region_model ())
    2156                 :       73442 :             if (const builtin_known_function *builtin_kf
    2157                 :       36721 :                 = old_model->get_builtin_kf (call))
    2158                 :        2441 :               fndecl = builtin_kf->builtin_decl ();
    2159                 :             : 
    2160                 :             :           /* Handle "__attribute__((malloc(FOO)))".   */
    2161                 :       73442 :           if (const deallocator_set *deallocators
    2162                 :             :               = mutable_this->get_or_create_custom_deallocator_set
    2163                 :       36721 :                   (fndecl))
    2164                 :             :             {
    2165                 :         260 :               tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
    2166                 :         260 :               bool returns_nonnull
    2167                 :         260 :                 = lookup_attribute ("returns_nonnull", attrs);
    2168                 :         260 :               on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull);
    2169                 :             :             }
    2170                 :             : 
    2171                 :       36721 :           {
    2172                 :             :             /* Handle "__attribute__((nonnull))".   */
    2173                 :       36721 :             tree fntype = TREE_TYPE (fndecl);
    2174                 :       36721 :             bitmap nonnull_args = get_nonnull_args (fntype);
    2175                 :       36721 :             if (nonnull_args)
    2176                 :             :               {
    2177                 :       13715 :                 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
    2178                 :             :                   {
    2179                 :        8971 :                     tree arg = gimple_call_arg (stmt, i);
    2180                 :        8971 :                     if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
    2181                 :        1390 :                       continue;
    2182                 :             :                     /* If we have a nonnull-args, and either all pointers, or
    2183                 :             :                        just the specified pointers.  */
    2184                 :        7581 :                     if (bitmap_empty_p (nonnull_args)
    2185                 :        7581 :                         || bitmap_bit_p (nonnull_args, i))
    2186                 :        6010 :                       handle_nonnull (sm_ctxt, node, stmt, fndecl, arg, i);
    2187                 :             :                   }
    2188                 :        4744 :                 BITMAP_FREE (nonnull_args);
    2189                 :             :               }
    2190                 :             :             /* Handle __attribute__((nonnull_if_nonzero (x, y))).  */
    2191                 :       36721 :             if (fntype)
    2192                 :       36721 :               for (tree attrs = TYPE_ATTRIBUTES (fntype);
    2193                 :       38738 :                    (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
    2194                 :        2017 :                    attrs = TREE_CHAIN (attrs))
    2195                 :             :                 {
    2196                 :        2017 :                   tree args = TREE_VALUE (attrs);
    2197                 :        2017 :                   unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
    2198                 :        2017 :                   unsigned int idx2
    2199                 :        2017 :                     = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
    2200                 :        2017 :                   unsigned int idx3 = idx2;
    2201                 :        2017 :                   if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args)))
    2202                 :         253 :                     idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1;
    2203                 :        2017 :                   if (idx < gimple_call_num_args (stmt)
    2204                 :        2017 :                       && idx2 < gimple_call_num_args (stmt)
    2205                 :        4034 :                       && idx3 < gimple_call_num_args (stmt))
    2206                 :             :                     {
    2207                 :        2017 :                       tree arg = gimple_call_arg (stmt, idx);
    2208                 :        2017 :                       tree arg2 = gimple_call_arg (stmt, idx2);
    2209                 :        2017 :                       tree arg3 = gimple_call_arg (stmt, idx3);
    2210                 :        2017 :                       if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE
    2211                 :        2017 :                           || !INTEGRAL_TYPE_P (TREE_TYPE (arg2))
    2212                 :        2017 :                           || !INTEGRAL_TYPE_P (TREE_TYPE (arg3))
    2213                 :        2017 :                           || integer_zerop (arg2)
    2214                 :        4012 :                           || integer_zerop (arg3))
    2215                 :          22 :                         continue;
    2216                 :        1995 :                       if (integer_nonzerop (arg2) && integer_nonzerop (arg3))
    2217                 :             :                         ;
    2218                 :             :                       else
    2219                 :             :                         /* FIXME: Use ranger here to query arg2 and arg3
    2220                 :             :                            ranges?  */
    2221                 :         904 :                         continue;
    2222                 :        1091 :                       handle_nonnull (sm_ctxt, node, stmt, fndecl, arg, idx);
    2223                 :             :                     }
    2224                 :             :                 }
    2225                 :             :           }
    2226                 :             : 
    2227                 :             :           /* Check for this after nonnull, so that if we have both
    2228                 :             :              then we transition to "freed", rather than "checked".  */
    2229                 :       36721 :           unsigned dealloc_argno = fndecl_dealloc_argno (fndecl);
    2230                 :       36721 :           if (dealloc_argno != UINT_MAX)
    2231                 :             :             {
    2232                 :         220 :               const deallocator *d
    2233                 :         220 :                 = mutable_this->get_or_create_deallocator (fndecl);
    2234                 :         220 :               on_deallocator_call (sm_ctxt, node, call, d, dealloc_argno);
    2235                 :             :             }
    2236                 :             :         }
    2237                 :             :       }
    2238                 :             : 
    2239                 :             :   /* Look for pointers explicitly being compared against zero
    2240                 :             :      that are in state assumed_non_null i.e. we already defererenced
    2241                 :             :      them.
    2242                 :             :      We have to do this check here, rather than in on_condition
    2243                 :             :      because we add a constraint that the pointer is non-null when
    2244                 :             :      dereferencing it, and this makes the apply_constraints_for_gcond
    2245                 :             :      find known-true and known-false conditions; on_condition is only
    2246                 :             :      called when adding new constraints.  */
    2247                 :      250152 :   if (const gcond *cond_stmt = dyn_cast <const gcond *> (stmt))
    2248                 :             :     {
    2249                 :       20113 :       enum tree_code op = gimple_cond_code (cond_stmt);
    2250                 :       20113 :       if (op == EQ_EXPR || op == NE_EXPR)
    2251                 :             :         {
    2252                 :       14788 :           tree lhs = gimple_cond_lhs (cond_stmt);
    2253                 :       14788 :           tree rhs = gimple_cond_rhs (cond_stmt);
    2254                 :       14788 :           if (any_pointer_p (lhs)
    2255                 :        6141 :               && any_pointer_p (rhs)
    2256                 :       20929 :               && zerop (rhs))
    2257                 :             :             {
    2258                 :        5633 :               state_t state = sm_ctxt.get_state (stmt, lhs);
    2259                 :        5633 :               if (assumed_non_null_p (state))
    2260                 :         224 :                 maybe_complain_about_deref_before_check
    2261                 :         224 :                   (sm_ctxt, node,
    2262                 :             :                    stmt,
    2263                 :             :                    (const assumed_non_null_state *)state,
    2264                 :             :                    lhs);
    2265                 :             :             }
    2266                 :             :         }
    2267                 :             :     }
    2268                 :             : 
    2269                 :      250152 :   if (tree lhs = sm_ctxt.is_zero_assignment (stmt))
    2270                 :       15258 :     if (any_pointer_p (lhs))
    2271                 :        3388 :       on_zero_assignment (sm_ctxt, stmt,lhs);
    2272                 :             : 
    2273                 :             :   /* Handle dereferences.  */
    2274                 :      910598 :   for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
    2275                 :             :     {
    2276                 :      660446 :       tree op = gimple_op (stmt, i);
    2277                 :      660446 :       if (!op)
    2278                 :      129914 :         continue;
    2279                 :      530532 :       if (TREE_CODE (op) == COMPONENT_REF)
    2280                 :       20218 :         op = TREE_OPERAND (op, 0);
    2281                 :             : 
    2282                 :      530532 :       if (TREE_CODE (op) == MEM_REF)
    2283                 :             :         {
    2284                 :       33997 :           tree arg = TREE_OPERAND (op, 0);
    2285                 :             : 
    2286                 :       33997 :           state_t state = sm_ctxt.get_state (stmt, arg);
    2287                 :       33997 :           if (state == m_start)
    2288                 :       20905 :             maybe_assume_non_null (sm_ctxt, arg, stmt);
    2289                 :       13092 :           else if (unchecked_p (state))
    2290                 :             :             {
    2291                 :        1040 :               tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2292                 :        1040 :               sm_ctxt.warn (node, stmt, arg,
    2293                 :        1040 :                             std::make_unique<possible_null_deref> (*this,
    2294                 :             :                                                                    diag_arg));
    2295                 :        1040 :               const allocation_state *astate = as_a_allocation_state (state);
    2296                 :        1040 :               sm_ctxt.set_next_state (stmt, arg, astate->get_nonnull ());
    2297                 :             :             }
    2298                 :       12052 :           else if (state == m_null)
    2299                 :             :             {
    2300                 :         332 :               tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2301                 :         332 :               sm_ctxt.warn (node, stmt, arg,
    2302                 :         332 :                             std::make_unique<null_deref> (*this, diag_arg));
    2303                 :         332 :               sm_ctxt.set_next_state (stmt, arg, m_stop);
    2304                 :             :             }
    2305                 :      660620 :           else if (freed_p (state))
    2306                 :             :             {
    2307                 :         174 :               tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2308                 :         174 :               const allocation_state *astate = as_a_allocation_state (state);
    2309                 :         174 :               sm_ctxt.warn (node, stmt, arg,
    2310                 :             :                             std::make_unique<use_after_free>
    2311                 :         174 :                               (*this, diag_arg, astate->m_deallocator));
    2312                 :         174 :               sm_ctxt.set_next_state (stmt, arg, m_stop);
    2313                 :             :             }
    2314                 :             :         }
    2315                 :             :     }
    2316                 :             :   return false;
    2317                 :             : }
    2318                 :             : 
    2319                 :             : /* Given a check against null of PTR in assumed-non-null state STATE,
    2320                 :             :    potentially add a deref_before_check warning to SM_CTXT.  */
    2321                 :             : 
    2322                 :             : void
    2323                 :         224 : malloc_state_machine::
    2324                 :             : maybe_complain_about_deref_before_check (sm_context &sm_ctxt,
    2325                 :             :                                          const supernode *node,
    2326                 :             :                                          const gimple *stmt,
    2327                 :             :                                          const assumed_non_null_state *state,
    2328                 :             :                                          tree ptr) const
    2329                 :             : {
    2330                 :         224 :   const region_model *model = sm_ctxt.get_old_region_model ();
    2331                 :         224 :   if (!model)
    2332                 :          69 :     return;
    2333                 :             : 
    2334                 :             :   /* Don't complain if the current frame (where the check is occurring) is
    2335                 :             :      deeper than the frame in which the "not null" assumption was made.
    2336                 :             :      This suppress false positives for cases like:
    2337                 :             : 
    2338                 :             :         void foo (struct s *p)
    2339                 :             :         {
    2340                 :             :           int val = s->some_field; // deref here
    2341                 :             :           shared_helper (p);
    2342                 :             :         }
    2343                 :             : 
    2344                 :             :      where "shared_helper" has:
    2345                 :             : 
    2346                 :             :         void shared_helper (struct s *p)
    2347                 :             :         {
    2348                 :             :           if (!p) // check here
    2349                 :             :             return;
    2350                 :             :           // etc
    2351                 :             :         }
    2352                 :             : 
    2353                 :             :      since the check in "shared_helper" is OK.  */
    2354                 :         224 :   const frame_region *checked_in_frame = model->get_current_frame ();
    2355                 :         224 :   const frame_region *assumed_nonnull_in_frame = state->m_frame;
    2356                 :         224 :   if (checked_in_frame->get_index () > assumed_nonnull_in_frame->get_index ())
    2357                 :             :     return;
    2358                 :             : 
    2359                 :             :   /* Don't complain if STMT was inlined from another function, to avoid
    2360                 :             :      similar false positives involving shared helper functions.  */
    2361                 :         163 :   if (stmt->location)
    2362                 :             :     {
    2363                 :         163 :       inlining_info info (stmt->location);
    2364                 :         163 :       if (info.get_extra_frames () > 0)
    2365                 :           8 :         return;
    2366                 :             :     }
    2367                 :             : 
    2368                 :         155 :   tree diag_ptr = sm_ctxt.get_diagnostic_tree (ptr);
    2369                 :         155 :   if (diag_ptr)
    2370                 :         155 :     sm_ctxt.warn
    2371                 :         155 :       (node, stmt, ptr,
    2372                 :         155 :        std::make_unique<deref_before_check> (*this, diag_ptr));
    2373                 :         155 :   sm_ctxt.set_next_state (stmt, ptr, m_stop);
    2374                 :             : }
    2375                 :             : 
    2376                 :             : /* Handle a call to an allocator.
    2377                 :             :    RETURNS_NONNULL is true if CALL is to a fndecl known to have
    2378                 :             :    __attribute__((returns_nonnull)).  */
    2379                 :             : 
    2380                 :             : void
    2381                 :        7598 : malloc_state_machine::on_allocator_call (sm_context &sm_ctxt,
    2382                 :             :                                          const gcall &call,
    2383                 :             :                                          const deallocator_set *deallocators,
    2384                 :             :                                          bool returns_nonnull) const
    2385                 :             : {
    2386                 :        7598 :   tree lhs = gimple_call_lhs (&call);
    2387                 :        7598 :   if (lhs)
    2388                 :             :     {
    2389                 :        7598 :       if (sm_ctxt.get_state (&call, lhs) == m_start)
    2390                 :        7572 :         sm_ctxt.set_next_state (&call, lhs,
    2391                 :             :                                 (returns_nonnull
    2392                 :             :                                  ? deallocators->m_nonnull
    2393                 :             :                                  : deallocators->m_unchecked));
    2394                 :             :     }
    2395                 :             :   else
    2396                 :             :     {
    2397                 :             :       /* TODO: report leak.  */
    2398                 :             :     }
    2399                 :        7598 : }
    2400                 :             : 
    2401                 :             : /* Handle deallocations of non-heap pointers.
    2402                 :             :    non-heap -> stop, with warning.  */
    2403                 :             : 
    2404                 :             : void
    2405                 :          53 : malloc_state_machine::handle_free_of_non_heap (sm_context &sm_ctxt,
    2406                 :             :                                                const supernode *node,
    2407                 :             :                                                const gcall &call,
    2408                 :             :                                                tree arg,
    2409                 :             :                                                const deallocator *d) const
    2410                 :             : {
    2411                 :          53 :   tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2412                 :          53 :   const region *freed_reg = nullptr;
    2413                 :          53 :   if (const program_state *old_state = sm_ctxt.get_old_program_state ())
    2414                 :             :     {
    2415                 :          53 :       const region_model *old_model = old_state->m_region_model;
    2416                 :          53 :       const svalue *ptr_sval = old_model->get_rvalue (arg, nullptr);
    2417                 :          53 :       freed_reg = old_model->deref_rvalue (ptr_sval, arg, nullptr);
    2418                 :             :     }
    2419                 :          53 :   sm_ctxt.warn (node, &call, arg,
    2420                 :             :                 std::make_unique<free_of_non_heap>
    2421                 :          53 :                   (*this, diag_arg, freed_reg, d->m_name));
    2422                 :          53 :   sm_ctxt.set_next_state (&call, arg, m_stop);
    2423                 :          53 : }
    2424                 :             : 
    2425                 :             : void
    2426                 :       12024 : malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt,
    2427                 :             :                                            const supernode *node,
    2428                 :             :                                            const gcall &call,
    2429                 :             :                                            const deallocator *d,
    2430                 :             :                                            unsigned argno) const
    2431                 :             : {
    2432                 :       12024 :   if (argno >= gimple_call_num_args (&call))
    2433                 :             :     return;
    2434                 :       12024 :   tree arg = gimple_call_arg (&call, argno);
    2435                 :             : 
    2436                 :       12024 :   state_t state = sm_ctxt.get_state (&call, arg);
    2437                 :             : 
    2438                 :             :   /* start/assumed_non_null/unchecked/nonnull -> freed.  */
    2439                 :       22692 :   if (state == m_start || assumed_non_null_p (state))
    2440                 :        1392 :     sm_ctxt.set_next_state (&call, arg, d->m_freed);
    2441                 :       10632 :   else if (unchecked_p (state) || nonnull_p (state))
    2442                 :             :     {
    2443                 :        5808 :       const allocation_state *astate = as_a_allocation_state (state);
    2444                 :        5808 :       gcc_assert (astate->m_deallocators);
    2445                 :        5808 :       if (!astate->m_deallocators->contains_p (d))
    2446                 :             :         {
    2447                 :             :           /* Wrong allocator.  */
    2448                 :          78 :           tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2449                 :          78 :           sm_ctxt.warn (node, &call, arg,
    2450                 :             :                         std::make_unique<mismatching_deallocation>
    2451                 :          78 :                           (*this, diag_arg,
    2452                 :          78 :                            astate->m_deallocators,
    2453                 :             :                            d));
    2454                 :             :         }
    2455                 :        5808 :       sm_ctxt.set_next_state (&call, arg, d->m_freed);
    2456                 :             :     }
    2457                 :             : 
    2458                 :             :   /* Keep state "null" as-is, rather than transitioning to "freed";
    2459                 :             :      we don't want to complain about double-free of NULL.  */
    2460                 :        4824 :   else if (state == d->m_freed)
    2461                 :             :     {
    2462                 :             :       /* freed -> stop, with warning.  */
    2463                 :        4473 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2464                 :        4473 :       sm_ctxt.warn (node, &call, arg,
    2465                 :        4473 :                     std::make_unique<double_free> (*this, diag_arg, d->m_name));
    2466                 :        4473 :       sm_ctxt.set_next_state (&call, arg, m_stop);
    2467                 :             :     }
    2468                 :         351 :   else if (state == m_non_heap)
    2469                 :             :     {
    2470                 :             :       /* non-heap -> stop, with warning.  */
    2471                 :          49 :       handle_free_of_non_heap (sm_ctxt, node, call, arg, d);
    2472                 :             :     }
    2473                 :             : }
    2474                 :             : 
    2475                 :             : /* Handle a call to "realloc".
    2476                 :             :    Check for free of non-heap or mismatching allocators,
    2477                 :             :    transitioning to the "stop" state for such cases.
    2478                 :             : 
    2479                 :             :    Otherwise, kf_realloc::impl_call_post will later
    2480                 :             :    get called (which will handle other sm-state transitions
    2481                 :             :    when the state is bifurcated).  */
    2482                 :             : 
    2483                 :             : void
    2484                 :         460 : malloc_state_machine::on_realloc_call (sm_context &sm_ctxt,
    2485                 :             :                                        const supernode *node,
    2486                 :             :                                        const gcall &call) const
    2487                 :             : {
    2488                 :         460 :   const unsigned argno = 0;
    2489                 :         460 :   const deallocator *d = &m_realloc;
    2490                 :             : 
    2491                 :         460 :   tree arg = gimple_call_arg (&call, argno);
    2492                 :             : 
    2493                 :         460 :   state_t state = sm_ctxt.get_state (&call, arg);
    2494                 :             : 
    2495                 :         460 :   if (unchecked_p (state) || nonnull_p (state))
    2496                 :             :     {
    2497                 :         205 :       const allocation_state *astate = as_a_allocation_state (state);
    2498                 :         205 :       gcc_assert (astate->m_deallocators);
    2499                 :         205 :       if (!astate->m_deallocators->contains_p (&m_free.m_deallocator))
    2500                 :             :         {
    2501                 :             :           /* Wrong allocator.  */
    2502                 :          16 :           tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2503                 :          16 :           sm_ctxt.warn (node, &call, arg,
    2504                 :             :                         std::make_unique<mismatching_deallocation>
    2505                 :          16 :                           (*this, diag_arg,
    2506                 :          16 :                            astate->m_deallocators, d));
    2507                 :          16 :           sm_ctxt.set_next_state (&call, arg, m_stop);
    2508                 :          16 :           if (path_context *path_ctxt = sm_ctxt.get_path_context ())
    2509                 :          16 :             path_ctxt->terminate_path ();
    2510                 :             :         }
    2511                 :             :     }
    2512                 :         255 :   else if (state == m_free.m_deallocator.m_freed)
    2513                 :             :     {
    2514                 :             :       /* freed -> stop, with warning.  */
    2515                 :           4 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
    2516                 :           4 :       sm_ctxt.warn (node, &call, arg,
    2517                 :           4 :                     std::make_unique<double_free> (*this, diag_arg, "free"));
    2518                 :           4 :       sm_ctxt.set_next_state (&call, arg, m_stop);
    2519                 :           4 :       if (path_context *path_ctxt = sm_ctxt.get_path_context ())
    2520                 :           4 :         path_ctxt->terminate_path ();
    2521                 :             :     }
    2522                 :         251 :   else if (state == m_non_heap)
    2523                 :             :     {
    2524                 :             :       /* non-heap -> stop, with warning.  */
    2525                 :           4 :       handle_free_of_non_heap (sm_ctxt, node, call, arg, d);
    2526                 :           4 :       if (path_context *path_ctxt = sm_ctxt.get_path_context ())
    2527                 :           4 :         path_ctxt->terminate_path ();
    2528                 :             :     }
    2529                 :         460 : }
    2530                 :             : 
    2531                 :             : /* Implementation of state_machine::on_phi vfunc for malloc_state_machine.  */
    2532                 :             : 
    2533                 :             : void
    2534                 :       24021 : malloc_state_machine::on_phi (sm_context &sm_ctxt,
    2535                 :             :                               const supernode *node ATTRIBUTE_UNUSED,
    2536                 :             :                               const gphi *phi,
    2537                 :             :                               tree rhs) const
    2538                 :             : {
    2539                 :       24021 :   if (zerop (rhs))
    2540                 :             :     {
    2541                 :         783 :       tree lhs = gimple_phi_result (phi);
    2542                 :         783 :       on_zero_assignment (sm_ctxt, phi, lhs);
    2543                 :             :     }
    2544                 :       24021 : }
    2545                 :             : 
    2546                 :             : /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
    2547                 :             :    Potentially transition state 'unchecked' to 'nonnull' or to 'null'.  */
    2548                 :             : 
    2549                 :             : void
    2550                 :       30204 : malloc_state_machine::on_condition (sm_context &sm_ctxt,
    2551                 :             :                                     const supernode *node ATTRIBUTE_UNUSED,
    2552                 :             :                                     const gimple *stmt,
    2553                 :             :                                     const svalue *lhs,
    2554                 :             :                                     enum tree_code op,
    2555                 :             :                                     const svalue *rhs) const
    2556                 :             : {
    2557                 :       30204 :   if (!rhs->all_zeroes_p ())
    2558                 :             :     return;
    2559                 :             : 
    2560                 :       19596 :   if (!any_pointer_p (lhs))
    2561                 :             :     return;
    2562                 :        8052 :   if (!any_pointer_p (rhs))
    2563                 :             :     return;
    2564                 :             : 
    2565                 :        8052 :   if (op == NE_EXPR)
    2566                 :             :     {
    2567                 :        4423 :       log ("got 'ARG != 0' match");
    2568                 :        4423 :       state_t s = sm_ctxt.get_state (stmt, lhs);
    2569                 :        4423 :       if (unchecked_p (s))
    2570                 :             :         {
    2571                 :        1123 :           const allocation_state *astate = as_a_allocation_state (s);
    2572                 :        1123 :           sm_ctxt.set_next_state (stmt, lhs, astate->get_nonnull ());
    2573                 :             :         }
    2574                 :             :     }
    2575                 :        3629 :   else if (op == EQ_EXPR)
    2576                 :             :     {
    2577                 :        3629 :       log ("got 'ARG == 0' match");
    2578                 :        3629 :       state_t s = sm_ctxt.get_state (stmt, lhs);
    2579                 :        3629 :       if (unchecked_p (s))
    2580                 :        1106 :         sm_ctxt.set_next_state (stmt, lhs, m_null);
    2581                 :             :     }
    2582                 :             : }
    2583                 :             : 
    2584                 :             : /* Implementation of state_machine::on_pop_frame vfunc for malloc_state_machine.
    2585                 :             :    Clear any "assumed-non-null" state where the assumption happened in
    2586                 :             :    FRAME_REG.  */
    2587                 :             : 
    2588                 :             : void
    2589                 :       23514 : malloc_state_machine::on_pop_frame (sm_state_map *smap,
    2590                 :             :                                     const frame_region *frame_reg) const
    2591                 :             : {
    2592                 :       23514 :   hash_set<const svalue *> svals_to_clear;
    2593                 :       34830 :   for (auto kv : *smap)
    2594                 :             :     {
    2595                 :       11316 :       const svalue *sval = kv.first;
    2596                 :       11316 :       state_t state = kv.second.m_state;
    2597                 :       15941 :       if (assumed_non_null_p (state))
    2598                 :             :         {
    2599                 :        4625 :           const assumed_non_null_state *assumed_state
    2600                 :             :             = (const assumed_non_null_state *)state;
    2601                 :        4625 :           if (frame_reg == assumed_state->m_frame)
    2602                 :        2715 :             svals_to_clear.add (sval);
    2603                 :             :         }
    2604                 :             :     }
    2605                 :       49743 :   for (auto sval : svals_to_clear)
    2606                 :        2715 :     smap->clear_any_state (sval);
    2607                 :       23514 : }
    2608                 :             : 
    2609                 :             : /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
    2610                 :             :    Don't allow purging of pointers in state 'unchecked' or 'nonnull'
    2611                 :             :    (to avoid false leak reports).  */
    2612                 :             : 
    2613                 :             : bool
    2614                 :     1105824 : malloc_state_machine::can_purge_p (state_t s) const
    2615                 :             : {
    2616                 :     1105824 :   enum resource_state rs = get_rs (s);
    2617                 :     1105824 :   return rs != RS_UNCHECKED && rs != RS_NONNULL;
    2618                 :             : }
    2619                 :             : 
    2620                 :             : /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
    2621                 :             :    (for complaining about leaks of pointers in state 'unchecked' and
    2622                 :             :    'nonnull').  */
    2623                 :             : 
    2624                 :             : std::unique_ptr<pending_diagnostic>
    2625                 :        1009 : malloc_state_machine::on_leak (tree var,
    2626                 :             :                                const program_state *,
    2627                 :             :                                const program_state *new_state) const
    2628                 :             : {
    2629                 :        1009 :   return std::make_unique<malloc_leak> (*this, var, new_state);
    2630                 :             : }
    2631                 :             : 
    2632                 :             : /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
    2633                 :             :    for malloc_state_machine.  */
    2634                 :             : 
    2635                 :             : bool
    2636                 :       31505 : malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s,
    2637                 :             :                                                          bool is_mutable) const
    2638                 :             : {
    2639                 :             :   /* An on-stack ptr doesn't stop being stack-allocated when passed to an
    2640                 :             :      unknown fn.  */
    2641                 :       31505 :   if (s == m_non_heap)
    2642                 :           0 :     return false;
    2643                 :             : 
    2644                 :             :   /* Otherwise, pointers passed as non-const can be freed.  */
    2645                 :             :   return is_mutable;
    2646                 :             : }
    2647                 :             : 
    2648                 :             : /* Implementation of state_machine::maybe_get_merged_states_nonequal vfunc
    2649                 :             :    for malloc_state_machine.
    2650                 :             : 
    2651                 :             :    Support discarding "assumed-non-null" states when merging with
    2652                 :             :    start state.  */
    2653                 :             : 
    2654                 :             : state_machine::state_t
    2655                 :      182050 : malloc_state_machine::maybe_get_merged_states_nonequal (state_t state_a,
    2656                 :             :                                                         state_t state_b) const
    2657                 :             : {
    2658                 :      182050 :   if (assumed_non_null_p (state_a) && state_b == m_start)
    2659                 :             :     return m_start;
    2660                 :      211881 :   if (state_a == m_start && assumed_non_null_p (state_b))
    2661                 :       17644 :     return m_start;
    2662                 :             :   return nullptr;
    2663                 :             : }
    2664                 :             : 
    2665                 :             : /* Return true if calls to FNDECL are known to not affect this sm-state.  */
    2666                 :             : 
    2667                 :             : bool
    2668                 :       36729 : malloc_state_machine::unaffected_by_call_p (tree fndecl)
    2669                 :             : {
    2670                 :             :   /* A set of functions that are known to not affect allocation
    2671                 :             :      status, even if we haven't fully modelled the rest of their
    2672                 :             :      behavior yet.  */
    2673                 :       36729 :   static const char * const funcnames[] = {
    2674                 :             :     /* This array must be kept sorted.  */
    2675                 :             :     "strsep",
    2676                 :             :   };
    2677                 :       36729 :   const size_t count = ARRAY_SIZE (funcnames);
    2678                 :       36729 :   function_set fs (funcnames, count);
    2679                 :             : 
    2680                 :       36729 :   if (fs.contains_decl_p (fndecl))
    2681                 :             :     return true;
    2682                 :             : 
    2683                 :             :   return false;
    2684                 :             : }
    2685                 :             : 
    2686                 :             : /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
    2687                 :             :    assign zero to LHS.  */
    2688                 :             : 
    2689                 :             : void
    2690                 :        4171 : malloc_state_machine::on_zero_assignment (sm_context &sm_ctxt,
    2691                 :             :                                           const gimple *stmt,
    2692                 :             :                                           tree lhs) const
    2693                 :             : {
    2694                 :        4171 :   state_t s = sm_ctxt.get_state (stmt, lhs);
    2695                 :        4171 :   enum resource_state rs = get_rs (s);
    2696                 :        4171 :   if (rs == RS_START
    2697                 :        4171 :       || rs == RS_UNCHECKED
    2698                 :             :       || rs == RS_NONNULL
    2699                 :         144 :       || rs == RS_FREED)
    2700                 :        4164 :     sm_ctxt.set_next_state (stmt, lhs, m_null);
    2701                 :        4171 : }
    2702                 :             : 
    2703                 :             : /* Special-case hook for handling realloc, for the "success with move to
    2704                 :             :    a new buffer" case, marking OLD_PTR_SVAL as freed and NEW_PTR_SVAL as
    2705                 :             :    non-null.
    2706                 :             : 
    2707                 :             :    This is similar to on_deallocator_call and on_allocator_call,
    2708                 :             :    but the checks happen in on_realloc_call, and by splitting the states.  */
    2709                 :             : 
    2710                 :             : void
    2711                 :         307 : malloc_state_machine::
    2712                 :             : on_realloc_with_move (region_model *model,
    2713                 :             :                       sm_state_map *smap,
    2714                 :             :                       const svalue *old_ptr_sval,
    2715                 :             :                       const svalue *new_ptr_sval,
    2716                 :             :                       const extrinsic_state &ext_state) const
    2717                 :             : {
    2718                 :         307 :   smap->set_state (model, old_ptr_sval,
    2719                 :         307 :                    m_free.m_deallocator.m_freed,
    2720                 :             :                    nullptr, ext_state);
    2721                 :             : 
    2722                 :         307 :   smap->set_state (model, new_ptr_sval,
    2723                 :         307 :                    m_free.m_nonnull,
    2724                 :             :                    nullptr, ext_state);
    2725                 :         307 : }
    2726                 :             : 
    2727                 :             : /*  Hook for get_or_create_region_for_heap_alloc for the case when we want
    2728                 :             :    ptr_sval to mark a newly created region as assumed non null on malloc SM.  */
    2729                 :             : void
    2730                 :           0 : malloc_state_machine::transition_ptr_sval_non_null (region_model *model,
    2731                 :             :     sm_state_map *smap,
    2732                 :             :     const svalue *new_ptr_sval,
    2733                 :             :     const extrinsic_state &ext_state) const
    2734                 :             : {
    2735                 :           0 :   smap->set_state (model, new_ptr_sval, m_free.m_nonnull, nullptr, ext_state);
    2736                 :           0 : }
    2737                 :             : 
    2738                 :             : static enum diagnostics::state_graphs::node_dynalloc_state
    2739                 :          44 : get_dynalloc_state_for_state (enum resource_state rs)
    2740                 :             : {
    2741                 :          44 :   switch (rs)
    2742                 :             :     {
    2743                 :           0 :     default:
    2744                 :           0 :       gcc_unreachable ();
    2745                 :             :     case RS_START:
    2746                 :             :     case RS_NULL:
    2747                 :             :     case RS_NON_HEAP:
    2748                 :             :     case RS_STOP:
    2749                 :             :       return diagnostics::state_graphs::node_dynalloc_state::unknown;
    2750                 :             : 
    2751                 :             :     case RS_ASSUMED_NON_NULL:
    2752                 :             :       return diagnostics::state_graphs::node_dynalloc_state::nonnull;
    2753                 :             : 
    2754                 :             :     case RS_UNCHECKED:
    2755                 :             :       return diagnostics::state_graphs::node_dynalloc_state::unchecked;
    2756                 :             :     case RS_NONNULL:
    2757                 :             :       return diagnostics::state_graphs::node_dynalloc_state::nonnull;
    2758                 :             :     case RS_FREED:
    2759                 :             :       return diagnostics::state_graphs::node_dynalloc_state::freed;
    2760                 :             :     }
    2761                 :             : }
    2762                 :             : 
    2763                 :             : void
    2764                 :          44 : malloc_state_machine::
    2765                 :             : add_state_to_state_graph (analyzer_state_graph &out_state_graph,
    2766                 :             :                           const svalue &sval,
    2767                 :             :                           state_machine::state_t state) const
    2768                 :             : {
    2769                 :          44 :   if (const region *reg = sval.maybe_get_region ())
    2770                 :             :     {
    2771                 :          44 :       auto reg_node = out_state_graph.get_or_create_state_node (*reg);
    2772                 :          44 :       auto alloc_state = as_a_allocation_state (state);
    2773                 :          44 :       gcc_assert (alloc_state);
    2774                 :             : 
    2775                 :          44 :       reg_node.set_dynalloc_state
    2776                 :          44 :         (get_dynalloc_state_for_state (alloc_state->m_rs));
    2777                 :          44 :       if (alloc_state->m_deallocators)
    2778                 :             :         {
    2779                 :          42 :           pretty_printer pp;
    2780                 :          42 :           alloc_state->m_deallocators->dump_to_pp (&pp);
    2781                 :          42 :           reg_node.m_node.set_attr (STATE_NODE_PREFIX,
    2782                 :             :                                     "expected-deallocators",
    2783                 :             :                                     pp_formatted_text (&pp));
    2784                 :          42 :         }
    2785                 :          44 :       if (alloc_state->m_deallocator)
    2786                 :           2 :         reg_node.m_node.set_attr (STATE_NODE_PREFIX,
    2787                 :             :                                   "deallocator",
    2788                 :           2 :                                   alloc_state->m_deallocator->m_name);
    2789                 :             :     }
    2790                 :          44 : }
    2791                 :             : 
    2792                 :             : } // anonymous namespace
    2793                 :             : 
    2794                 :             : /* Internal interface to this file. */
    2795                 :             : 
    2796                 :             : std::unique_ptr<state_machine>
    2797                 :        3320 : make_malloc_state_machine (logger *logger)
    2798                 :             : {
    2799                 :        3320 :   return std::make_unique<malloc_state_machine> (logger);
    2800                 :             : }
    2801                 :             : 
    2802                 :             : /* Specialcase hook for handling realloc, for use by
    2803                 :             :    kf_realloc::impl_call_post::success_with_move::update_model.  */
    2804                 :             : 
    2805                 :             : void
    2806                 :         437 : region_model::on_realloc_with_move (const call_details &cd,
    2807                 :             :                                     const svalue *old_ptr_sval,
    2808                 :             :                                     const svalue *new_ptr_sval)
    2809                 :             : {
    2810                 :         437 :   region_model_context *ctxt = cd.get_ctxt ();
    2811                 :         437 :   if (!ctxt)
    2812                 :         130 :     return;
    2813                 :         309 :   const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2814                 :         309 :   if (!ext_state)
    2815                 :             :     return;
    2816                 :             : 
    2817                 :         307 :   sm_state_map *smap;
    2818                 :         307 :   const state_machine *sm;
    2819                 :         307 :   unsigned sm_idx;
    2820                 :         307 :   if (!ctxt->get_malloc_map (&smap, &sm, &sm_idx))
    2821                 :             :     return;
    2822                 :             : 
    2823                 :         307 :   gcc_assert (smap);
    2824                 :         307 :   gcc_assert (sm);
    2825                 :             : 
    2826                 :         307 :   const malloc_state_machine &malloc_sm
    2827                 :             :     = (const malloc_state_machine &)*sm;
    2828                 :             : 
    2829                 :         307 :   malloc_sm.on_realloc_with_move (this,
    2830                 :             :                                   smap,
    2831                 :             :                                   old_ptr_sval,
    2832                 :             :                                   new_ptr_sval,
    2833                 :             :                                   *ext_state);
    2834                 :             : }
    2835                 :             : 
    2836                 :             : /* Moves ptr_sval from start to assumed non-null, for use by
    2837                 :             :    region_model::get_or_create_region_for_heap_alloc.  */
    2838                 :             : void
    2839                 :           0 : region_model::transition_ptr_sval_non_null (region_model_context *ctxt,
    2840                 :             : const svalue *ptr_sval)
    2841                 :             : {
    2842                 :           0 :   if (!ctxt)
    2843                 :           0 :     return;
    2844                 :           0 :   const extrinsic_state *ext_state = ctxt->get_ext_state ();
    2845                 :           0 :   if (!ext_state)
    2846                 :             :     return;
    2847                 :             : 
    2848                 :           0 :   sm_state_map *smap;
    2849                 :           0 :   const state_machine *sm;
    2850                 :           0 :   unsigned sm_idx;
    2851                 :           0 :   if (!ctxt->get_malloc_map (&smap, &sm, &sm_idx))
    2852                 :             :     return;
    2853                 :             : 
    2854                 :           0 :   gcc_assert (smap);
    2855                 :           0 :   gcc_assert (sm);
    2856                 :             : 
    2857                 :           0 :   const malloc_state_machine &malloc_sm = (const malloc_state_machine &)*sm;
    2858                 :             : 
    2859                 :           0 :   malloc_sm.transition_ptr_sval_non_null (this, smap, ptr_sval, *ext_state);
    2860                 :             : }
    2861                 :             : 
    2862                 :             : } // namespace ana
    2863                 :             : 
    2864                 :             : #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.