(root)/
gcc-13.2.0/
gcc/
analyzer/
sm.h
       1  /* Modeling API uses and misuses via state machines.
       2     Copyright (C) 2019-2023 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  #ifndef GCC_ANALYZER_SM_H
      22  #define GCC_ANALYZER_SM_H
      23  
      24  /* Utility functions for use by state machines.  */
      25  
      26  namespace ana {
      27  
      28  class state_machine;
      29  class sm_context;
      30  class pending_diagnostic;
      31  
      32  extern bool any_pointer_p (tree expr);
      33  extern bool any_pointer_p (const svalue *sval);
      34  
      35  /* An abstract base class for a state machine describing an API.
      36     Manages a set of state objects, and has various virtual functions
      37     for pattern-matching on statements.  */
      38  
      39  class state_machine : public log_user
      40  {
      41  public:
      42    /* States are represented by immutable objects, owned by the state
      43       machine.  */
      44    class state
      45    {
      46    public:
      47      state (const char *name, unsigned id) : m_name (name), m_id (id) {}
      48      virtual ~state () {}
      49  
      50      const char *get_name () const { return m_name; }
      51      virtual void dump_to_pp (pretty_printer *pp) const;
      52      virtual json::value *to_json () const;
      53  
      54      unsigned get_id () const { return m_id; }
      55  
      56    private:
      57      const char *m_name;
      58      unsigned m_id;
      59    };
      60    typedef const state_machine::state *state_t;
      61  
      62    state_machine (const char *name, logger *logger);
      63    virtual ~state_machine () {}
      64  
      65    /* Should states be inherited from a parent region to a child region,
      66       when first accessing a child region?
      67       For example we should inherit the taintedness of a subregion,
      68       but we should not inherit the "malloc:non-null" state of a field
      69       within a heap-allocated struct.  */
      70    virtual bool inherited_state_p () const = 0;
      71  
      72    /* A vfunc for more general handling of inheritance.  */
      73    virtual state_t
      74    alt_get_inherited_state (const sm_state_map &,
      75  			   const svalue *,
      76  			   const extrinsic_state &) const
      77    {
      78      return NULL;
      79    }
      80  
      81    virtual state_machine::state_t get_default_state (const svalue *) const
      82    {
      83      return m_start;
      84    }
      85  
      86    const char *get_name () const { return m_name; }
      87  
      88    state_t get_state_by_name (const char *name) const;
      89  
      90    /* Return true if STMT is a function call recognized by this sm.  */
      91    virtual bool on_stmt (sm_context *sm_ctxt,
      92  			const supernode *node,
      93  			const gimple *stmt) const = 0;
      94  
      95    virtual void on_phi (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
      96  		       const supernode *node ATTRIBUTE_UNUSED,
      97  		       const gphi *phi ATTRIBUTE_UNUSED,
      98  		       tree rhs ATTRIBUTE_UNUSED) const
      99    {
     100    }
     101  
     102    virtual void on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
     103  			     const supernode *node ATTRIBUTE_UNUSED,
     104  			     const gimple *stmt ATTRIBUTE_UNUSED,
     105  			     const svalue *lhs ATTRIBUTE_UNUSED,
     106  			     enum tree_code op ATTRIBUTE_UNUSED,
     107  			     const svalue *rhs ATTRIBUTE_UNUSED) const
     108    {
     109    }
     110  
     111    virtual void
     112    on_bounded_ranges (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
     113  		     const supernode *node ATTRIBUTE_UNUSED,
     114  		     const gimple *stmt ATTRIBUTE_UNUSED,
     115  		     const svalue &sval ATTRIBUTE_UNUSED,
     116  		     const bounded_ranges &ranges ATTRIBUTE_UNUSED) const
     117    {
     118    }
     119  
     120    virtual void
     121    on_pop_frame (sm_state_map *smap ATTRIBUTE_UNUSED,
     122  		const frame_region *frame_reg ATTRIBUTE_UNUSED) const
     123    {
     124    }
     125  
     126    /* Return true if it safe to discard the given state (to help
     127       when simplifying state objects).
     128       States that need leak detection should return false.  */
     129    virtual bool can_purge_p (state_t s) const = 0;
     130  
     131    /* Called when VAR leaks (and !can_purge_p).  */
     132    virtual std::unique_ptr<pending_diagnostic>
     133    on_leak (tree var ATTRIBUTE_UNUSED) const;
     134  
     135    /* Return true if S should be reset to "start" for values passed (or reachable
     136       from) calls to unknown functions.  IS_MUTABLE is true for pointers as
     137       non-const, false if only passed as const-pointers.
     138  
     139       For example, in sm-malloc.cc, an on-stack ptr doesn't stop being
     140       stack-allocated when passed to an unknown fn, but a malloc-ed pointer
     141       could be freed when passed to an unknown fn (unless passed as "const").  */
     142    virtual bool reset_when_passed_to_unknown_fn_p (state_t s ATTRIBUTE_UNUSED,
     143  						  bool is_mutable) const
     144    {
     145      return is_mutable;
     146    }
     147  
     148    /* Attempt to get a state for the merger of STATE_A and STATE_B,
     149       or return NULL if merging shouldn't occur, so that differences
     150       between sm-state will lead to separate exploded nodes.
     151  
     152       Most state machines will only merge equal states, but can
     153       override maybe_get_merged_states_nonequal to support mergers
     154       of certain non-equal states.  */
     155    state_t maybe_get_merged_state (state_t state_a,
     156  				  state_t state_b) const
     157    {
     158      if (state_a == state_b)
     159        return state_a;
     160      return maybe_get_merged_states_nonequal (state_a, state_b);
     161    }
     162  
     163    /* Base implementation of hook for maybe_get_merged_state on non-equal
     164       states.  */
     165    virtual state_t
     166    maybe_get_merged_states_nonequal (state_t state_a ATTRIBUTE_UNUSED,
     167  				    state_t state_b ATTRIBUTE_UNUSED) const
     168    {
     169      /* By default, non-equal sm states should inhibit merger of enodes.  */
     170      return NULL;
     171    }
     172  
     173    void validate (state_t s) const;
     174  
     175    void dump_to_pp (pretty_printer *pp) const;
     176  
     177    json::object *to_json () const;
     178  
     179    state_t get_start_state () const { return m_start; }
     180  
     181  protected:
     182    state_t add_state (const char *name);
     183    state_t add_custom_state (state *s)
     184    {
     185      m_states.safe_push (s);
     186      return s;
     187    }
     188  
     189    unsigned alloc_state_id () { return m_next_state_id++; }
     190  
     191  private:
     192    DISABLE_COPY_AND_ASSIGN (state_machine);
     193  
     194    const char *m_name;
     195  
     196    /* States are owned by the state_machine.  */
     197    auto_delete_vec<state> m_states;
     198  
     199    unsigned m_next_state_id;
     200  
     201  protected:
     202    /* Must be inited after m_next_state_id.  */
     203    state_t m_start;
     204  };
     205  
     206  /* Abstract base class for state machines to pass to
     207     sm_context::on_custom_transition for handling non-standard transitions
     208     (e.g. adding a node and edge to simulate registering a callback and having
     209     the callback be called later).  */
     210  
     211  class custom_transition
     212  {
     213  public:
     214    virtual ~custom_transition () {}
     215    virtual void impl_transition (exploded_graph *eg,
     216  				exploded_node *src_enode,
     217  				int sm_idx) = 0;
     218  };
     219  
     220  /* Abstract base class giving an interface for the state machine to call
     221     the checker engine, at a particular stmt.  */
     222  
     223  class sm_context
     224  {
     225  public:
     226    virtual ~sm_context () {}
     227  
     228    /* Get the fndecl used at call, or NULL_TREE.
     229       Use in preference to gimple_call_fndecl (and gimple_call_addr_fndecl),
     230       since it can look through function pointer assignments and
     231       other callback handling.  */
     232    virtual tree get_fndecl_for_call (const gcall *call) = 0;
     233  
     234    /* Get the old state of VAR at STMT.  */
     235    virtual state_machine::state_t get_state (const gimple *stmt,
     236  					    tree var) = 0;
     237    virtual state_machine::state_t get_state (const gimple *stmt,
     238  					    const svalue *) = 0;
     239    /* Set the next state of VAR to be TO, recording the "origin" of the
     240       state as ORIGIN.
     241       Use STMT for location information.  */
     242    virtual void set_next_state (const gimple *stmt,
     243  			       tree var,
     244  			       state_machine::state_t to,
     245  			       tree origin = NULL_TREE) = 0;
     246    virtual void set_next_state (const gimple *stmt,
     247  			       const svalue *var,
     248  			       state_machine::state_t to,
     249  			       tree origin = NULL_TREE) = 0;
     250  
     251    /* Called by state_machine in response to pattern matches:
     252       if VAR is in state FROM, transition it to state TO, potentially
     253       recording the "origin" of the state as ORIGIN.
     254       Use NODE and STMT for location information.  */
     255    void on_transition (const supernode *node ATTRIBUTE_UNUSED,
     256  		      const gimple *stmt,
     257  		      tree var,
     258  		      state_machine::state_t from,
     259  		      state_machine::state_t to,
     260  		      tree origin = NULL_TREE)
     261    {
     262      state_machine::state_t current = get_state (stmt, var);
     263      if (current == from)
     264        set_next_state (stmt, var, to, origin);
     265    }
     266  
     267    void on_transition (const supernode *node ATTRIBUTE_UNUSED,
     268  		      const gimple *stmt,
     269  		      const svalue *var,
     270  		      state_machine::state_t from,
     271  		      state_machine::state_t to,
     272  		      tree origin = NULL_TREE)
     273    {
     274      state_machine::state_t current = get_state (stmt, var);
     275      if (current == from)
     276        set_next_state (stmt, var, to, origin);
     277    }
     278  
     279    /* Called by state_machine in response to pattern matches:
     280       issue a diagnostic D using NODE and STMT for location information.  */
     281    virtual void warn (const supernode *node, const gimple *stmt,
     282  		     tree var,
     283  		     std::unique_ptr<pending_diagnostic> d) = 0;
     284    virtual void warn (const supernode *node, const gimple *stmt,
     285  		     const svalue *var,
     286  		     std::unique_ptr<pending_diagnostic> d) = 0;
     287  
     288    /* For use when generating trees when creating pending_diagnostics, so that
     289       rather than e.g.
     290         "double-free of '<unknown>'"
     291       we can print:
     292         "double-free of 'inbuf.data'".  */
     293    virtual tree get_diagnostic_tree (tree expr)
     294    {
     295      return expr;
     296    }
     297    virtual tree get_diagnostic_tree (const svalue *) = 0;
     298  
     299    virtual state_machine::state_t get_global_state () const = 0;
     300    virtual void set_global_state (state_machine::state_t) = 0;
     301  
     302    /* A vfunc for handling custom transitions, such as when registering
     303       a signal handler.  */
     304    virtual void on_custom_transition (custom_transition *transition) = 0;
     305  
     306    /* If STMT is an assignment known to assign zero to its LHS, return
     307       the LHS.
     308       Otherwise return NULL_TREE.  */
     309    virtual tree is_zero_assignment (const gimple *stmt) = 0;
     310  
     311    virtual path_context *get_path_context () const
     312    {
     313      return NULL;
     314    }
     315  
     316    /* Are we handling an external function with unknown side effects?  */
     317    virtual bool unknown_side_effects_p () const { return false; }
     318  
     319    virtual const program_state *get_old_program_state () const = 0;
     320    virtual const program_state *get_new_program_state () const = 0;
     321  
     322    const region_model *get_old_region_model () const;
     323  
     324  protected:
     325    sm_context (int sm_idx, const state_machine &sm)
     326    : m_sm_idx (sm_idx), m_sm (sm) {}
     327  
     328    int m_sm_idx;
     329    const state_machine &m_sm;
     330  };
     331  
     332  
     333  /* The various state_machine subclasses are hidden in their respective
     334     implementation files.  */
     335  
     336  extern void make_checkers (auto_delete_vec <state_machine> &out,
     337  			   logger *logger);
     338  
     339  extern state_machine *make_malloc_state_machine (logger *logger);
     340  extern state_machine *make_fileptr_state_machine (logger *logger);
     341  extern state_machine *make_taint_state_machine (logger *logger);
     342  extern state_machine *make_sensitive_state_machine (logger *logger);
     343  extern state_machine *make_signal_state_machine (logger *logger);
     344  extern state_machine *make_pattern_test_state_machine (logger *logger);
     345  extern state_machine *make_va_list_state_machine (logger *logger);
     346  extern state_machine *make_fd_state_machine (logger *logger);
     347  
     348  } // namespace ana
     349  
     350  #endif /* GCC_ANALYZER_SM_H */