LCOV - code coverage report
Current view: top level - gcc/analyzer - sm-malloc.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 90.9 % 1063 966
Test Date: 2026-02-28 14:20:25 Functions: 94.9 % 138 131
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.