LCOV - code coverage report
Current view: top level - gcc/analyzer - sm-malloc.cc (source / functions) Hit Total Coverage
Test: gcc.info Lines: 316 326 96.9 %
Date: 2020-03-28 11:57:23 Functions: 47 47 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /* A state machine for detecting misuses of the malloc/free API.
       2                 :            :    Copyright (C) 2019-2020 Free Software Foundation, Inc.
       3                 :            :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       4                 :            : 
       5                 :            : This file is part of GCC.
       6                 :            : 
       7                 :            : GCC is free software; you can redistribute it and/or modify it
       8                 :            : under the terms of the GNU General Public License as published by
       9                 :            : the Free Software Foundation; either version 3, or (at your option)
      10                 :            : any later version.
      11                 :            : 
      12                 :            : GCC is distributed in the hope that it will be useful, but
      13                 :            : WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :            : General Public License for more details.
      16                 :            : 
      17                 :            : You should have received a copy of the GNU General Public License
      18                 :            : along with GCC; see the file COPYING3.  If not see
      19                 :            : <http://www.gnu.org/licenses/>.  */
      20                 :            : 
      21                 :            : #include "config.h"
      22                 :            : #include "system.h"
      23                 :            : #include "coretypes.h"
      24                 :            : #include "tree.h"
      25                 :            : #include "function.h"
      26                 :            : #include "basic-block.h"
      27                 :            : #include "gimple.h"
      28                 :            : #include "options.h"
      29                 :            : #include "bitmap.h"
      30                 :            : #include "diagnostic-path.h"
      31                 :            : #include "diagnostic-metadata.h"
      32                 :            : #include "function.h"
      33                 :            : #include "analyzer/analyzer.h"
      34                 :            : #include "diagnostic-event-id.h"
      35                 :            : #include "analyzer/analyzer-logging.h"
      36                 :            : #include "analyzer/sm.h"
      37                 :            : #include "analyzer/pending-diagnostic.h"
      38                 :            : 
      39                 :            : #if ENABLE_ANALYZER
      40                 :            : 
      41                 :            : namespace ana {
      42                 :            : 
      43                 :            : namespace {
      44                 :            : 
      45                 :            : /* A state machine for detecting misuses of the malloc/free API.
      46                 :            : 
      47                 :            :    See sm-malloc.dot for an overview (keep this in-sync with that file).  */
      48                 :            : 
      49                 :            : class malloc_state_machine : public state_machine
      50                 :            : {
      51                 :            : public:
      52                 :            :   malloc_state_machine (logger *logger);
      53                 :            : 
      54                 :       1920 :   bool inherited_state_p () const FINAL OVERRIDE { return false; }
      55                 :            : 
      56                 :            :   bool on_stmt (sm_context *sm_ctxt,
      57                 :            :                 const supernode *node,
      58                 :            :                 const gimple *stmt) const FINAL OVERRIDE;
      59                 :            : 
      60                 :            :   void on_phi (sm_context *sm_ctxt,
      61                 :            :                const supernode *node,
      62                 :            :                const gphi *phi,
      63                 :            :                tree rhs) const FINAL OVERRIDE;
      64                 :            : 
      65                 :            :   void on_condition (sm_context *sm_ctxt,
      66                 :            :                      const supernode *node,
      67                 :            :                      const gimple *stmt,
      68                 :            :                      tree lhs,
      69                 :            :                      enum tree_code op,
      70                 :            :                      tree rhs) const FINAL OVERRIDE;
      71                 :            : 
      72                 :            :   bool can_purge_p (state_t s) const FINAL OVERRIDE;
      73                 :            :   pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
      74                 :            : 
      75                 :            :   /* Start state.  */
      76                 :            :   state_t m_start;
      77                 :            : 
      78                 :            :   /* State for a pointer returned from malloc that hasn't been checked for
      79                 :            :      NULL.
      80                 :            :      It could be a pointer to heap-allocated memory, or could be NULL.  */
      81                 :            :   state_t m_unchecked;
      82                 :            : 
      83                 :            :   /* State for a pointer that's known to be NULL.  */
      84                 :            :   state_t m_null;
      85                 :            : 
      86                 :            :   /* State for a pointer to heap-allocated memory, known to be non-NULL.  */
      87                 :            :   state_t m_nonnull;
      88                 :            : 
      89                 :            :   /* State for a pointer to freed memory.  */
      90                 :            :   state_t m_freed;
      91                 :            : 
      92                 :            :   /* State for a pointer that's known to not be on the heap (e.g. to a local
      93                 :            :      or global).  */
      94                 :            :   state_t m_non_heap; // TODO: or should this be a different state machine?
      95                 :            :   // or do we need child values etc?
      96                 :            : 
      97                 :            :   /* Stop state, for pointers we don't want to track any more.  */
      98                 :            :   state_t m_stop;
      99                 :            : 
     100                 :            : private:
     101                 :            :   void on_zero_assignment (sm_context *sm_ctxt,
     102                 :            :                            const supernode *node,
     103                 :            :                            const gimple *stmt,
     104                 :            :                            tree lhs) const;
     105                 :            : };
     106                 :            : 
     107                 :            : /* Class for diagnostics relating to malloc_state_machine.  */
     108                 :            : 
     109                 :            : class malloc_diagnostic : public pending_diagnostic
     110                 :            : {
     111                 :            : public:
     112                 :       5635 :   malloc_diagnostic (const malloc_state_machine &sm, tree arg)
     113                 :       5635 :   : m_sm (sm), m_arg (arg)
     114                 :            :   {}
     115                 :            : 
     116                 :        250 :   bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
     117                 :            :   {
     118                 :        250 :     return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
     119                 :            :   }
     120                 :            : 
     121                 :        142 :   label_text describe_state_change (const evdesc::state_change &change)
     122                 :            :     OVERRIDE
     123                 :            :   {
     124                 :        142 :     if (change.m_old_state == m_sm.m_start
     125                 :        142 :         && change.m_new_state == m_sm.m_unchecked)
     126                 :            :       // TODO: verify that it's the allocation stmt, not a copy
     127                 :         48 :       return label_text::borrow ("allocated here");
     128                 :         94 :     if (change.m_old_state == m_sm.m_unchecked
     129                 :         19 :         && change.m_new_state == m_sm.m_nonnull)
     130                 :         15 :       return change.formatted_print ("assuming %qE is non-NULL",
     131                 :         15 :                                      change.m_expr);
     132                 :         79 :     if (change.m_new_state == m_sm.m_null)
     133                 :            :       {
     134                 :         36 :         if (change.m_old_state == m_sm.m_unchecked)
     135                 :          4 :           return change.formatted_print ("assuming %qE is NULL",
     136                 :          4 :                                          change.m_expr);
     137                 :            :         else
     138                 :         32 :           return change.formatted_print ("%qE is NULL",
     139                 :         32 :                                          change.m_expr);
     140                 :            :       }
     141                 :            : 
     142                 :         43 :     return label_text ();
     143                 :            :   }
     144                 :            : 
     145                 :            : protected:
     146                 :            :   const malloc_state_machine &m_sm;
     147                 :            :   tree m_arg;
     148                 :            : };
     149                 :            : 
     150                 :            : /* Concrete subclass for reporting double-free diagnostics.  */
     151                 :            : 
     152                 :            : class double_free : public malloc_diagnostic
     153                 :            : {
     154                 :            : public:
     155                 :        649 :   double_free (const malloc_state_machine &sm, tree arg)
     156                 :        649 :   : malloc_diagnostic (sm, arg)
     157                 :            :   {}
     158                 :            : 
     159                 :        359 :   const char *get_kind () const FINAL OVERRIDE { return "double_free"; }
     160                 :            : 
     161                 :        115 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     162                 :            :   {
     163                 :        230 :     auto_diagnostic_group d;
     164                 :        115 :     diagnostic_metadata m;
     165                 :        115 :     m.add_cwe (415); /* CWE-415: Double Free.  */
     166                 :        115 :     return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
     167                 :        115 :                          "double-%<free%> of %qE", m_arg);
     168                 :            :   }
     169                 :            : 
     170                 :        276 :   label_text describe_state_change (const evdesc::state_change &change)
     171                 :            :     FINAL OVERRIDE
     172                 :            :   {
     173                 :        276 :     if (change.m_new_state == m_sm.m_freed)
     174                 :            :       {
     175                 :        228 :         m_first_free_event = change.m_event_id;
     176                 :        228 :         return change.formatted_print ("first %qs here", "free");
     177                 :            :       }
     178                 :         48 :     return malloc_diagnostic::describe_state_change (change);
     179                 :            :   }
     180                 :            : 
     181                 :         56 :   label_text describe_call_with_state (const evdesc::call_with_state &info)
     182                 :            :     FINAL OVERRIDE
     183                 :            :   {
     184                 :         56 :     if (info.m_state == m_sm.m_freed)
     185                 :         46 :       return info.formatted_print
     186                 :            :         ("passing freed pointer %qE in call to %qE from %qE",
     187                 :         46 :          info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
     188                 :         10 :     return label_text ();
     189                 :            :   }
     190                 :            : 
     191                 :        228 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     192                 :            :   {
     193                 :        228 :     if (m_first_free_event.known_p ())
     194                 :        228 :       return ev.formatted_print ("second %qs here; first %qs was at %@",
     195                 :            :                                  "free", "free",
     196                 :        228 :                                  &m_first_free_event);
     197                 :          0 :     return ev.formatted_print ("second %qs here", "free");
     198                 :            :   }
     199                 :            : 
     200                 :            : private:
     201                 :            :   diagnostic_event_id_t m_first_free_event;
     202                 :            : };
     203                 :            : 
     204                 :            : /* Abstract subclass for describing possible bad uses of NULL.
     205                 :            :    Responsible for describing the call that could return NULL.  */
     206                 :            : 
     207                 :            : class possible_null : public malloc_diagnostic
     208                 :            : {
     209                 :            : public:
     210                 :       1482 :   possible_null (const malloc_state_machine &sm, tree arg)
     211                 :       1482 :   : malloc_diagnostic (sm, arg)
     212                 :            :   {}
     213                 :            : 
     214                 :         54 :   label_text describe_state_change (const evdesc::state_change &change)
     215                 :            :     FINAL OVERRIDE
     216                 :            :   {
     217                 :         54 :     if (change.m_old_state == m_sm.m_start
     218                 :         54 :         && change.m_new_state == m_sm.m_unchecked)
     219                 :            :       {
     220                 :         54 :         m_origin_of_unchecked_event = change.m_event_id;
     221                 :         54 :         return label_text::borrow ("this call could return NULL");
     222                 :            :       }
     223                 :          0 :     return malloc_diagnostic::describe_state_change (change);
     224                 :            :   }
     225                 :            : 
     226                 :          8 :   label_text describe_return_of_state (const evdesc::return_of_state &info)
     227                 :            :     FINAL OVERRIDE
     228                 :            :   {
     229                 :          8 :     if (info.m_state == m_sm.m_unchecked)
     230                 :          8 :       return info.formatted_print ("possible return of NULL to %qE from %qE",
     231                 :          8 :                                    info.m_caller_fndecl, info.m_callee_fndecl);
     232                 :          0 :     return label_text ();
     233                 :            :   }
     234                 :            : 
     235                 :            : protected:
     236                 :            :   diagnostic_event_id_t m_origin_of_unchecked_event;
     237                 :            : };
     238                 :            : 
     239                 :            : /* Concrete subclass for describing dereference of a possible NULL
     240                 :            :    value.  */
     241                 :            : 
     242                 :            : class possible_null_deref : public possible_null
     243                 :            : {
     244                 :            : public:
     245                 :       1239 :   possible_null_deref (const malloc_state_machine &sm, tree arg)
     246                 :       1239 :   : possible_null (sm, arg)
     247                 :            :   {}
     248                 :            : 
     249                 :         60 :   const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; }
     250                 :            : 
     251                 :         20 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     252                 :            :   {
     253                 :            :     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
     254                 :         20 :     diagnostic_metadata m;
     255                 :         20 :     m.add_cwe (690);
     256                 :         20 :     return warning_meta (rich_loc, m,
     257                 :            :                          OPT_Wanalyzer_possible_null_dereference,
     258                 :         20 :                          "dereference of possibly-NULL %qE", m_arg);
     259                 :            :   }
     260                 :            : 
     261                 :         39 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     262                 :            :   {
     263                 :         39 :     if (m_origin_of_unchecked_event.known_p ())
     264                 :         39 :       return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
     265                 :         39 :                                  ev.m_expr,
     266                 :         39 :                                  &m_origin_of_unchecked_event);
     267                 :            :     else
     268                 :          0 :       return ev.formatted_print ("%qE could be NULL", ev.m_expr);
     269                 :            :   }
     270                 :            : 
     271                 :            : };
     272                 :            : 
     273                 :            : /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
     274                 :            :    Issue a note informing that the pertinent argument must be non-NULL.  */
     275                 :            : 
     276                 :            : static void
     277                 :          8 : inform_nonnull_attribute (tree fndecl, int arg_idx)
     278                 :            : {
     279                 :          8 :   inform (DECL_SOURCE_LOCATION (fndecl),
     280                 :            :           "argument %u of %qD must be non-null",
     281                 :            :           arg_idx + 1, fndecl);
     282                 :            :   /* Ideally we would use the location of the parm and underline the
     283                 :            :      attribute also - but we don't have the location_t values at this point
     284                 :            :      in the middle-end.
     285                 :            :      For reference, the C and C++ FEs have get_fndecl_argument_location.  */
     286                 :          8 : }
     287                 :            : 
     288                 :            : /* Concrete subclass for describing passing a possibly-NULL value to a
     289                 :            :    function marked with __attribute__((nonnull)).  */
     290                 :            : 
     291                 :            : class possible_null_arg : public possible_null
     292                 :            : {
     293                 :            : public:
     294                 :        243 :   possible_null_arg (const malloc_state_machine &sm, tree arg,
     295                 :            :                      tree fndecl, int arg_idx)
     296                 :        243 :   : possible_null (sm, arg),
     297                 :        243 :     m_fndecl (fndecl), m_arg_idx (arg_idx)
     298                 :            :   {}
     299                 :            : 
     300                 :         18 :   const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
     301                 :            : 
     302                 :          6 :   bool subclass_equal_p (const pending_diagnostic &base_other) const
     303                 :            :   {
     304                 :          6 :     const possible_null_arg &sub_other
     305                 :            :       = (const possible_null_arg &)base_other;
     306                 :          6 :     return (same_tree_p (m_arg, sub_other.m_arg)
     307                 :          6 :             && m_fndecl == sub_other.m_fndecl
     308                 :         12 :             && m_arg_idx == sub_other.m_arg_idx);
     309                 :            :   }
     310                 :            : 
     311                 :            : 
     312                 :          6 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     313                 :            :   {
     314                 :            :     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
     315                 :          6 :     auto_diagnostic_group d;
     316                 :          6 :     diagnostic_metadata m;
     317                 :          6 :     m.add_cwe (690);
     318                 :          6 :     bool warned
     319                 :          6 :       = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
     320                 :            :                       "use of possibly-NULL %qE where non-null expected",
     321                 :            :                       m_arg);
     322                 :          6 :     if (warned)
     323                 :          6 :       inform_nonnull_attribute (m_fndecl, m_arg_idx);
     324                 :          6 :     return warned;
     325                 :            :   }
     326                 :            : 
     327                 :         12 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     328                 :            :   {
     329                 :         12 :     if (m_origin_of_unchecked_event.known_p ())
     330                 :         12 :       return ev.formatted_print ("argument %u (%qE) from %@ could be NULL"
     331                 :            :                                  " where non-null expected",
     332                 :         12 :                                  m_arg_idx + 1, ev.m_expr,
     333                 :         12 :                                  &m_origin_of_unchecked_event);
     334                 :            :     else
     335                 :          0 :       return ev.formatted_print ("argument %u (%qE) could be NULL"
     336                 :            :                                  " where non-null expected",
     337                 :          0 :                                  m_arg_idx + 1, ev.m_expr);
     338                 :            :   }
     339                 :            : 
     340                 :            : private:
     341                 :            :   tree m_fndecl;
     342                 :            :   int m_arg_idx;
     343                 :            : };
     344                 :            : 
     345                 :            : /* Concrete subclass for describing a dereference of a NULL value.  */
     346                 :            : 
     347                 :            : class null_deref : public malloc_diagnostic
     348                 :            : {
     349                 :            : public:
     350                 :       1239 :   null_deref (const malloc_state_machine &sm, tree arg)
     351                 :       1239 :   : malloc_diagnostic (sm, arg) {}
     352                 :            : 
     353                 :         75 :   const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
     354                 :            : 
     355                 :         21 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     356                 :            :   {
     357                 :            :     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
     358                 :         21 :     diagnostic_metadata m;
     359                 :         21 :     m.add_cwe (690);
     360                 :         21 :     return warning_meta (rich_loc, m,
     361                 :            :                          OPT_Wanalyzer_null_dereference,
     362                 :         21 :                          "dereference of NULL %qE", m_arg);
     363                 :            :   }
     364                 :            : 
     365                 :          8 :   label_text describe_return_of_state (const evdesc::return_of_state &info)
     366                 :            :     FINAL OVERRIDE
     367                 :            :   {
     368                 :          8 :     if (info.m_state == m_sm.m_null)
     369                 :          8 :       return info.formatted_print ("return of NULL to %qE from %qE",
     370                 :          8 :                                    info.m_caller_fndecl, info.m_callee_fndecl);
     371                 :          0 :     return label_text ();
     372                 :            :   }
     373                 :            : 
     374                 :         41 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     375                 :            :   {
     376                 :         41 :     return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
     377                 :            :   }
     378                 :            : };
     379                 :            : 
     380                 :            : /* Concrete subclass for describing passing a NULL value to a
     381                 :            :    function marked with __attribute__((nonnull)).  */
     382                 :            : 
     383                 :            : class null_arg : public malloc_diagnostic
     384                 :            : {
     385                 :            : public:
     386                 :        243 :   null_arg (const malloc_state_machine &sm, tree arg,
     387                 :            :             tree fndecl, int arg_idx)
     388                 :        243 :   : malloc_diagnostic (sm, arg),
     389                 :        243 :     m_fndecl (fndecl), m_arg_idx (arg_idx)
     390                 :            :   {}
     391                 :            : 
     392                 :          6 :   const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
     393                 :            : 
     394                 :          2 :   bool subclass_equal_p (const pending_diagnostic &base_other) const
     395                 :            :   {
     396                 :          2 :     const null_arg &sub_other
     397                 :            :       = (const null_arg &)base_other;
     398                 :          2 :     return (same_tree_p (m_arg, sub_other.m_arg)
     399                 :          2 :             && m_fndecl == sub_other.m_fndecl
     400                 :          4 :             && m_arg_idx == sub_other.m_arg_idx);
     401                 :            :   }
     402                 :            : 
     403                 :          2 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     404                 :            :   {
     405                 :            :     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
     406                 :          2 :     auto_diagnostic_group d;
     407                 :          2 :     diagnostic_metadata m;
     408                 :          2 :     m.add_cwe (690);
     409                 :          2 :     bool warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
     410                 :            :                                 "use of NULL %qE where non-null expected",
     411                 :            :                                 m_arg);
     412                 :          2 :     if (warned)
     413                 :          2 :       inform_nonnull_attribute (m_fndecl, m_arg_idx);
     414                 :          2 :     return warned;
     415                 :            :   }
     416                 :            : 
     417                 :          4 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     418                 :            :   {
     419                 :          4 :     return ev.formatted_print ("argument %u (%qE) NULL"
     420                 :            :                                " where non-null expected",
     421                 :          4 :                                m_arg_idx + 1, ev.m_expr);
     422                 :            :   }
     423                 :            : 
     424                 :            : private:
     425                 :            :   tree m_fndecl;
     426                 :            :   int m_arg_idx;
     427                 :            : };
     428                 :            : 
     429                 :            : class use_after_free : public malloc_diagnostic
     430                 :            : {
     431                 :            : public:
     432                 :       1239 :   use_after_free (const malloc_state_machine &sm, tree arg)
     433                 :       1239 :   : malloc_diagnostic (sm, arg)
     434                 :            :   {}
     435                 :            : 
     436                 :          9 :   const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
     437                 :            : 
     438                 :          3 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     439                 :            :   {
     440                 :            :     /* CWE-416: Use After Free.  */
     441                 :          3 :     diagnostic_metadata m;
     442                 :          3 :     m.add_cwe (416);
     443                 :          3 :     return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
     444                 :          3 :                          "use after %<free%> of %qE", m_arg);
     445                 :            :   }
     446                 :            : 
     447                 :         14 :   label_text describe_state_change (const evdesc::state_change &change)
     448                 :            :     FINAL OVERRIDE
     449                 :            :   {
     450                 :         14 :     if (change.m_new_state == m_sm.m_freed)
     451                 :            :       {
     452                 :          6 :         m_free_event = change.m_event_id;
     453                 :          6 :         return label_text::borrow ("freed here");
     454                 :            :       }
     455                 :          8 :     return malloc_diagnostic::describe_state_change (change);
     456                 :            :   }
     457                 :            : 
     458                 :          6 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     459                 :            :   {
     460                 :          6 :     if (m_free_event.known_p ())
     461                 :          6 :       return ev.formatted_print ("use after %<free%> of %qE; freed at %@",
     462                 :          6 :                                  ev.m_expr, &m_free_event);
     463                 :            :     else
     464                 :          0 :       return ev.formatted_print ("use after %<free%> of %qE", ev.m_expr);
     465                 :            :   }
     466                 :            : 
     467                 :            : private:
     468                 :            :   diagnostic_event_id_t m_free_event;
     469                 :            : };
     470                 :            : 
     471                 :            : class malloc_leak : public malloc_diagnostic
     472                 :            : {
     473                 :            : public:
     474                 :        134 :   malloc_leak (const malloc_state_machine &sm, tree arg)
     475                 :        134 :   : malloc_diagnostic (sm, arg) {}
     476                 :            : 
     477                 :        220 :   const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
     478                 :            : 
     479                 :         64 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     480                 :            :   {
     481                 :         64 :     diagnostic_metadata m;
     482                 :         64 :     m.add_cwe (401);
     483                 :         64 :     return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
     484                 :         64 :                          "leak of %qE", m_arg);
     485                 :            :   }
     486                 :            : 
     487                 :        199 :   label_text describe_state_change (const evdesc::state_change &change)
     488                 :            :     FINAL OVERRIDE
     489                 :            :   {
     490                 :        199 :     if (change.m_new_state == m_sm.m_unchecked)
     491                 :            :       {
     492                 :        153 :         m_malloc_event = change.m_event_id;
     493                 :        153 :         return label_text::borrow ("allocated here");
     494                 :            :       }
     495                 :         46 :     return malloc_diagnostic::describe_state_change (change);
     496                 :            :   }
     497                 :            : 
     498                 :        126 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     499                 :            :   {
     500                 :        126 :     if (m_malloc_event.known_p ())
     501                 :        112 :       return ev.formatted_print ("%qE leaks here; was allocated at %@",
     502                 :        112 :                                  ev.m_expr, &m_malloc_event);
     503                 :            :     else
     504                 :         14 :       return ev.formatted_print ("%qE leaks here", ev.m_expr);
     505                 :            :   }
     506                 :            : 
     507                 :            : private:
     508                 :            :   diagnostic_event_id_t m_malloc_event;
     509                 :            : };
     510                 :            : 
     511                 :            : class free_of_non_heap : public malloc_diagnostic
     512                 :            : {
     513                 :            : public:
     514                 :        649 :   free_of_non_heap (const malloc_state_machine &sm, tree arg)
     515                 :        649 :   : malloc_diagnostic (sm, arg), m_kind (KIND_UNKNOWN)
     516                 :            :   {
     517                 :            :   }
     518                 :            : 
     519                 :         15 :   const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
     520                 :            : 
     521                 :          5 :   bool subclass_equal_p (const pending_diagnostic &base_other) const
     522                 :            :     FINAL OVERRIDE
     523                 :            :   {
     524                 :          5 :     const free_of_non_heap &other = (const free_of_non_heap &)base_other;
     525                 :          5 :     return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
     526                 :            :   }
     527                 :            : 
     528                 :          5 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     529                 :            :   {
     530                 :          5 :     auto_diagnostic_group d;
     531                 :          5 :     diagnostic_metadata m;
     532                 :          5 :     m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap.  */
     533                 :          5 :     switch (m_kind)
     534                 :            :       {
     535                 :          0 :       default:
     536                 :          0 :         gcc_unreachable ();
     537                 :          3 :       case KIND_UNKNOWN:
     538                 :          3 :         return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
     539                 :            :                              "%<free%> of %qE which points to memory"
     540                 :            :                              " not on the heap",
     541                 :          3 :                              m_arg);
     542                 :          2 :         break;
     543                 :          2 :       case KIND_ALLOCA:
     544                 :          2 :         return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
     545                 :            :                              "%<free%> of memory allocated on the stack by"
     546                 :            :                              " %qs (%qE) will corrupt the heap",
     547                 :          2 :                              "alloca", m_arg);
     548                 :          5 :         break;
     549                 :            :       }
     550                 :            :   }
     551                 :            : 
     552                 :         10 :   label_text describe_state_change (const evdesc::state_change &change)
     553                 :            :     FINAL OVERRIDE
     554                 :            :   {
     555                 :            :     /* Attempt to reconstruct what kind of pointer it is.
     556                 :            :        (It seems neater for this to be a part of the state, though).  */
     557                 :         10 :     if (TREE_CODE (change.m_expr) == SSA_NAME)
     558                 :            :       {
     559                 :         10 :         gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
     560                 :         10 :         if (gcall *call = dyn_cast <gcall *> (def_stmt))
     561                 :            :           {
     562                 :          6 :             if (is_special_named_call_p (call, "alloca", 1)
     563                 :          6 :                 || is_special_named_call_p (call, "__builtin_alloca", 1))
     564                 :            :               {
     565                 :          4 :                 m_kind = KIND_ALLOCA;
     566                 :          4 :                 return label_text::borrow
     567                 :         10 :                   ("memory is allocated on the stack here");
     568                 :            :               }
     569                 :            :           }
     570                 :            :       }
     571                 :          6 :     return label_text::borrow ("pointer is from here");
     572                 :            :   }
     573                 :            : 
     574                 :         10 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     575                 :            :   {
     576                 :         10 :     return ev.formatted_print ("call to %qs here", "free");
     577                 :            :   }
     578                 :            : 
     579                 :            : private:
     580                 :            :   enum kind
     581                 :            :   {
     582                 :            :     KIND_UNKNOWN,
     583                 :            :     KIND_ALLOCA
     584                 :            :   };
     585                 :            :   enum kind m_kind;
     586                 :            : };
     587                 :            : 
     588                 :            : /* malloc_state_machine's ctor.  */
     589                 :            : 
     590                 :        381 : malloc_state_machine::malloc_state_machine (logger *logger)
     591                 :        381 : : state_machine ("malloc", logger)
     592                 :            : {
     593                 :        381 :   m_start = add_state ("start");
     594                 :        381 :   m_unchecked = add_state ("unchecked");
     595                 :        381 :   m_null = add_state ("null");
     596                 :        381 :   m_nonnull = add_state ("nonnull");
     597                 :        381 :   m_freed = add_state ("freed");
     598                 :        381 :   m_non_heap = add_state ("non-heap");
     599                 :        381 :   m_stop = add_state ("stop");
     600                 :        381 : }
     601                 :            : 
     602                 :            : /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine.  */
     603                 :            : 
     604                 :            : bool
     605                 :      15322 : malloc_state_machine::on_stmt (sm_context *sm_ctxt,
     606                 :            :                                const supernode *node,
     607                 :            :                                const gimple *stmt) const
     608                 :            : {
     609                 :      15322 :   if (const gcall *call = dyn_cast <const gcall *> (stmt))
     610                 :       3666 :     if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
     611                 :            :       {
     612                 :       3640 :         if (is_named_call_p (callee_fndecl, "malloc", call, 1)
     613                 :       3365 :             || is_named_call_p (callee_fndecl, "calloc", call, 2)
     614                 :       3343 :             || is_std_named_call_p (callee_fndecl, "malloc", call, 1)
     615                 :       3339 :             || is_std_named_call_p (callee_fndecl, "calloc", call, 2)
     616                 :       3335 :             || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1)
     617                 :       6961 :             || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2))
     618                 :            :           {
     619                 :        320 :             tree lhs = gimple_call_lhs (call);
     620                 :        320 :             if (lhs)
     621                 :            :               {
     622                 :        320 :                 lhs = sm_ctxt->get_readable_tree (lhs);
     623                 :        320 :                 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked);
     624                 :            :               }
     625                 :            :             else
     626                 :            :               {
     627                 :            :                 /* TODO: report leak.  */
     628                 :            :               }
     629                 :        320 :             return true;
     630                 :            :           }
     631                 :            : 
     632                 :       3320 :         if (is_named_call_p (callee_fndecl, "alloca", call, 1)
     633                 :       3320 :             || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
     634                 :            :           {
     635                 :          8 :             tree lhs = gimple_call_lhs (call);
     636                 :          8 :             if (lhs)
     637                 :            :               {
     638                 :          8 :                 lhs = sm_ctxt->get_readable_tree (lhs);
     639                 :          8 :                 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
     640                 :            :               }
     641                 :          8 :             return true;
     642                 :            :           }
     643                 :            : 
     644                 :       3312 :         if (is_named_call_p (callee_fndecl, "free", call, 1)
     645                 :       2704 :             || is_std_named_call_p (callee_fndecl, "free", call, 1)
     646                 :       6008 :             || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
     647                 :            :           {
     648                 :        649 :             tree arg = gimple_call_arg (call, 0);
     649                 :            : 
     650                 :        649 :             arg = sm_ctxt->get_readable_tree (arg);
     651                 :            : 
     652                 :            :             /* start/unchecked/nonnull -> freed.  */
     653                 :        649 :             sm_ctxt->on_transition (node, stmt, arg, m_start, m_freed);
     654                 :        649 :             sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_freed);
     655                 :        649 :             sm_ctxt->on_transition (node, stmt, arg, m_nonnull, m_freed);
     656                 :            : 
     657                 :            :             /* Keep state "null" as-is, rather than transitioning to "free";
     658                 :            :                we don't want to complain about double-free of NULL.  */
     659                 :            : 
     660                 :            :             /* freed -> stop, with warning.  */
     661                 :        649 :             sm_ctxt->warn_for_state (node, stmt, arg, m_freed,
     662                 :        649 :                                      new double_free (*this, arg));
     663                 :        649 :             sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop);
     664                 :            : 
     665                 :            :             /* non-heap -> stop, with warning.  */
     666                 :        649 :             sm_ctxt->warn_for_state (node, stmt, arg, m_non_heap,
     667                 :        649 :                                      new free_of_non_heap (*this, arg));
     668                 :        649 :             sm_ctxt->on_transition (node, stmt, arg, m_non_heap, m_stop);
     669                 :        649 :             return true;
     670                 :            :           }
     671                 :            : 
     672                 :            :         /* Handle "__attribute__((nonnull))".   */
     673                 :       2663 :         {
     674                 :       2663 :           tree fntype = TREE_TYPE (callee_fndecl);
     675                 :       2663 :           bitmap nonnull_args = get_nonnull_args (fntype);
     676                 :       2663 :           if (nonnull_args)
     677                 :            :             {
     678                 :        490 :               for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
     679                 :            :                 {
     680                 :        313 :                   tree arg = gimple_call_arg (stmt, i);
     681                 :        313 :                   if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
     682                 :         43 :                     continue;
     683                 :            :                   /* If we have a nonnull-args, and either all pointers, or just
     684                 :            :                      the specified pointers.  */
     685                 :        270 :                   if (bitmap_empty_p (nonnull_args)
     686                 :        270 :                       || bitmap_bit_p (nonnull_args, i))
     687                 :            :                     {
     688                 :        243 :                       sm_ctxt->warn_for_state
     689                 :        243 :                         (node, stmt, arg, m_unchecked,
     690                 :        243 :                          new possible_null_arg (*this, arg, callee_fndecl, i));
     691                 :        243 :                       sm_ctxt->on_transition (node, stmt, arg, m_unchecked,
     692                 :        243 :                                               m_nonnull);
     693                 :            : 
     694                 :        243 :                       sm_ctxt->warn_for_state
     695                 :        243 :                         (node, stmt, arg, m_null,
     696                 :        243 :                          new null_arg (*this, arg, callee_fndecl, i));
     697                 :        243 :                       sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
     698                 :            :                     }
     699                 :            :                 }
     700                 :        177 :               BITMAP_FREE (nonnull_args);
     701                 :            :             }
     702                 :            :         }
     703                 :            :       }
     704                 :            : 
     705                 :      14345 :   if (tree lhs = is_zero_assignment (stmt))
     706                 :        330 :     if (any_pointer_p (lhs))
     707                 :         79 :       on_zero_assignment (sm_ctxt, node, stmt,lhs);
     708                 :            : 
     709                 :      14345 :   if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
     710                 :            :     {
     711                 :       6697 :       enum tree_code op = gimple_assign_rhs_code (assign_stmt);
     712                 :       6697 :       if (op == ADDR_EXPR)
     713                 :            :         {
     714                 :        236 :           tree lhs = gimple_assign_lhs (assign_stmt);
     715                 :        236 :           if (lhs)
     716                 :            :             {
     717                 :        236 :               lhs = sm_ctxt->get_readable_tree (lhs);
     718                 :        236 :               sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
     719                 :            :             }
     720                 :            :         }
     721                 :            :     }
     722                 :            : 
     723                 :            :   /* Handle dereferences.  */
     724                 :      53099 :   for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
     725                 :            :     {
     726                 :      38754 :       tree op = gimple_op (stmt, i);
     727                 :      38754 :       if (!op)
     728                 :      10244 :         continue;
     729                 :      28510 :       if (TREE_CODE (op) == COMPONENT_REF)
     730                 :        985 :         op = TREE_OPERAND (op, 0);
     731                 :            : 
     732                 :      28510 :       if (TREE_CODE (op) == MEM_REF)
     733                 :            :         {
     734                 :       1239 :           tree arg = TREE_OPERAND (op, 0);
     735                 :       1239 :           arg = sm_ctxt->get_readable_tree (arg);
     736                 :            : 
     737                 :       1239 :           sm_ctxt->warn_for_state (node, stmt, arg, m_unchecked,
     738                 :       1239 :                                    new possible_null_deref (*this, arg));
     739                 :       1239 :           sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_nonnull);
     740                 :            : 
     741                 :       1239 :           sm_ctxt->warn_for_state (node, stmt, arg, m_null,
     742                 :       1239 :                                    new null_deref (*this, arg));
     743                 :       1239 :           sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
     744                 :            : 
     745                 :       1239 :           sm_ctxt->warn_for_state (node, stmt, arg, m_freed,
     746                 :       1239 :                                    new use_after_free (*this, arg));
     747                 :       1239 :           sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop);
     748                 :            :         }
     749                 :            :     }
     750                 :            :   return false;
     751                 :            : }
     752                 :            : 
     753                 :            : /* Implementation of state_machine::on_phi vfunc for malloc_state_machine.  */
     754                 :            : 
     755                 :            : void
     756                 :       2591 : malloc_state_machine::on_phi (sm_context *sm_ctxt,
     757                 :            :                               const supernode *node,
     758                 :            :                               const gphi *phi,
     759                 :            :                               tree rhs) const
     760                 :            : {
     761                 :       2591 :   if (zerop (rhs))
     762                 :            :     {
     763                 :        100 :       tree lhs = gimple_phi_result (phi);
     764                 :        100 :       on_zero_assignment (sm_ctxt, node, phi, lhs);
     765                 :            :     }
     766                 :       2591 : }
     767                 :            : 
     768                 :            : /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
     769                 :            :    Potentially transition state 'unchecked' to 'nonnull' or to 'null'.  */
     770                 :            : 
     771                 :            : void
     772                 :       5153 : malloc_state_machine::on_condition (sm_context *sm_ctxt,
     773                 :            :                                     const supernode *node,
     774                 :            :                                     const gimple *stmt,
     775                 :            :                                     tree lhs,
     776                 :            :                                     enum tree_code op,
     777                 :            :                                     tree rhs) const
     778                 :            : {
     779                 :       5153 :   if (!zerop (rhs))
     780                 :            :     return;
     781                 :            : 
     782                 :       1501 :   if (!any_pointer_p (lhs))
     783                 :            :     return;
     784                 :        338 :   if (!any_pointer_p (rhs))
     785                 :            :     return;
     786                 :            : 
     787                 :        338 :   if (op == NE_EXPR)
     788                 :            :     {
     789                 :        180 :       log ("got 'ARG != 0' match");
     790                 :        180 :       sm_ctxt->on_transition (node, stmt,
     791                 :        180 :                               lhs, m_unchecked, m_nonnull);
     792                 :            :     }
     793                 :        158 :   else if (op == EQ_EXPR)
     794                 :            :     {
     795                 :        158 :       log ("got 'ARG == 0' match");
     796                 :        158 :       sm_ctxt->on_transition (node, stmt,
     797                 :        158 :                               lhs, m_unchecked, m_null);
     798                 :            :     }
     799                 :            : }
     800                 :            : 
     801                 :            : /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
     802                 :            :    Don't allow purging of pointers in state 'unchecked' or 'nonnull'
     803                 :            :    (to avoid false leak reports).  */
     804                 :            : 
     805                 :            : bool
     806                 :      31050 : malloc_state_machine::can_purge_p (state_t s) const
     807                 :            : {
     808                 :      31050 :   return s != m_unchecked && s != m_nonnull;
     809                 :            : }
     810                 :            : 
     811                 :            : /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
     812                 :            :    (for complaining about leaks of pointers in state 'unchecked' and
     813                 :            :    'nonnull').  */
     814                 :            : 
     815                 :            : pending_diagnostic *
     816                 :        134 : malloc_state_machine::on_leak (tree var) const
     817                 :            : {
     818                 :        134 :   return new malloc_leak (*this, var);
     819                 :            : }
     820                 :            : 
     821                 :            : /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
     822                 :            :    assign zero to LHS.  */
     823                 :            : 
     824                 :            : void
     825                 :        179 : malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
     826                 :            :                                           const supernode *node,
     827                 :            :                                           const gimple *stmt,
     828                 :            :                                           tree lhs) const
     829                 :            : {
     830                 :        179 :   sm_ctxt->on_transition (node, stmt, lhs, m_start, m_null);
     831                 :        179 :   sm_ctxt->on_transition (node, stmt, lhs, m_unchecked, m_null);
     832                 :        179 :   sm_ctxt->on_transition (node, stmt, lhs, m_nonnull, m_null);
     833                 :        179 :   sm_ctxt->on_transition (node, stmt, lhs, m_freed, m_null);
     834                 :        179 : }
     835                 :            : 
     836                 :            : } // anonymous namespace
     837                 :            : 
     838                 :            : /* Internal interface to this file. */
     839                 :            : 
     840                 :            : state_machine *
     841                 :        381 : make_malloc_state_machine (logger *logger)
     842                 :            : {
     843                 :        381 :   return new malloc_state_machine (logger);
     844                 :            : }
     845                 :            : 
     846                 :            : } // namespace ana
     847                 :            : 
     848                 :            : #endif /* #if ENABLE_ANALYZER */

Generated by: LCOV version 1.0

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 --enable-host-shared. GCC test suite is run with the built compiler.