(root)/
gcc-13.2.0/
gcc/
testsuite/
gcc.dg/
plugin/
analyzer_kernel_plugin.c
       1  /* Proof-of-concept of a -fanalyzer plugin for the Linux kernel.  */
       2  /* { dg-options "-g" } */
       3  
       4  #define INCLUDE_MEMORY
       5  #include "gcc-plugin.h"
       6  #include "config.h"
       7  #include "system.h"
       8  #include "coretypes.h"
       9  #include "tree.h"
      10  #include "function.h"
      11  #include "basic-block.h"
      12  #include "gimple.h"
      13  #include "gimple-iterator.h"
      14  #include "diagnostic-core.h"
      15  #include "graphviz.h"
      16  #include "options.h"
      17  #include "cgraph.h"
      18  #include "tree-dfa.h"
      19  #include "stringpool.h"
      20  #include "convert.h"
      21  #include "target.h"
      22  #include "fold-const.h"
      23  #include "tree-pretty-print.h"
      24  #include "diagnostic-color.h"
      25  #include "diagnostic-metadata.h"
      26  #include "tristate.h"
      27  #include "bitmap.h"
      28  #include "selftest.h"
      29  #include "function.h"
      30  #include "json.h"
      31  #include "analyzer/analyzer.h"
      32  #include "analyzer/analyzer-logging.h"
      33  #include "ordered-hash-map.h"
      34  #include "options.h"
      35  #include "cgraph.h"
      36  #include "cfg.h"
      37  #include "digraph.h"
      38  #include "analyzer/supergraph.h"
      39  #include "sbitmap.h"
      40  #include "analyzer/call-string.h"
      41  #include "analyzer/program-point.h"
      42  #include "analyzer/store.h"
      43  #include "analyzer/region-model.h"
      44  #include "analyzer/call-details.h"
      45  #include "analyzer/call-info.h"
      46  #include "make-unique.h"
      47  
      48  int plugin_is_GPL_compatible;
      49  
      50  #if ENABLE_ANALYZER
      51  
      52  namespace ana {
      53  
      54  /* Implementation of "copy_from_user" and "copy_to_user".  */
      55    
      56  class copy_across_boundary_fn : public known_function
      57  {
      58   public:
      59    virtual bool untrusted_source_p () const = 0;
      60    virtual bool untrusted_destination_p () const = 0;
      61  
      62    bool matches_call_types_p (const call_details &cd) const final override
      63    {
      64      return cd.num_args () == 3;
      65    }
      66  
      67    void impl_call_pre (const call_details &cd) const final override
      68    {
      69      region_model_manager *mgr = cd.get_manager ();
      70      region_model *model = cd.get_model ();
      71      region_model_context *ctxt = cd.get_ctxt ();
      72  
      73      const svalue *dest_sval = cd.get_arg_svalue (0);
      74      const svalue *src_sval = cd.get_arg_svalue (1);
      75      const svalue *num_bytes_sval = cd.get_arg_svalue (2);
      76  
      77      const region *dest_reg = model->deref_rvalue (dest_sval,
      78  						  cd.get_arg_tree (0),
      79  						  ctxt);
      80      const region *src_reg = model->deref_rvalue (src_sval,
      81  						 cd.get_arg_tree (1),
      82  						 ctxt);
      83      if (const svalue *bounded_sval
      84  	  = model->maybe_get_copy_bounds (src_reg, num_bytes_sval))
      85        num_bytes_sval = bounded_sval;
      86  
      87      if (tree cst = num_bytes_sval->maybe_get_constant ())
      88        if (zerop (cst))
      89  	/* No-op.  */
      90  	return;
      91  
      92      const region *sized_src_reg = mgr->get_sized_region (src_reg,
      93  							 NULL_TREE,
      94  							 num_bytes_sval);
      95  
      96      const svalue *copied_sval
      97        = model->get_store_value (sized_src_reg, ctxt);
      98      const region *sized_dest_reg = mgr->get_sized_region (dest_reg,
      99  							  NULL_TREE,
     100  							  num_bytes_sval);
     101  
     102      if (ctxt)
     103        {
     104  	/* Bifurcate state, creating a "failure" out-edge.  */
     105  	ctxt->bifurcate (make_unique<copy_failure> (cd));
     106  
     107  	/* The "unbifurcated" state is the "success" case.  */
     108  	copy_success success (cd,
     109  			      sized_dest_reg,
     110  			      copied_sval,
     111  			      sized_src_reg,
     112  			      untrusted_source_p (),
     113  			      untrusted_destination_p ());
     114  	success.update_model (model, NULL, ctxt);
     115        }
     116    }
     117  
     118   private:
     119    class copy_success : public success_call_info
     120    {
     121    public:
     122      copy_success (const call_details &cd,
     123  		  const region *sized_dest_reg,
     124  		  const svalue *copied_sval,
     125  		  const region *sized_src_reg,
     126  		  bool untrusted_source,
     127  		  bool untrusted_destination)
     128      : success_call_info (cd),
     129        m_sized_dest_reg (sized_dest_reg),
     130        m_copied_sval (copied_sval),
     131        m_sized_src_reg (sized_src_reg),
     132        m_untrusted_source (untrusted_source),
     133        m_untrusted_destination (untrusted_destination)
     134      {}
     135  
     136      bool update_model (region_model *model,
     137  		       const exploded_edge *,
     138  		       region_model_context *ctxt) const final override
     139      {
     140        call_details cd (get_call_details (model, ctxt));
     141        model->update_for_zero_return (cd, true);
     142        model->set_value (m_sized_dest_reg, m_copied_sval, ctxt);
     143        if (ctxt && m_untrusted_source)
     144  	model->mark_as_tainted (m_copied_sval, ctxt);
     145        if (m_untrusted_destination)
     146  	model->maybe_complain_about_infoleak (m_sized_dest_reg,
     147  					      m_copied_sval,
     148  					      m_sized_src_reg,
     149  					      ctxt);
     150        return true;
     151      }
     152  
     153      const region *m_sized_dest_reg;
     154      const svalue *m_copied_sval;
     155      const region *m_sized_src_reg;
     156      bool m_untrusted_source;
     157      bool m_untrusted_destination;
     158    };
     159  
     160    class copy_failure : public failed_call_info
     161    {
     162    public:
     163      copy_failure (const call_details &cd)
     164      : failed_call_info (cd)
     165      {}
     166  
     167      bool update_model (region_model *model,
     168  		       const exploded_edge *,
     169  		       region_model_context *ctxt) const final override
     170      {
     171        call_details cd (get_call_details (model, ctxt));
     172        model->update_for_nonzero_return (cd);
     173        /* Leave the destination region untouched.  */
     174        return true;
     175      }
     176    };
     177  };
     178  
     179  /* "copy_from_user".  */
     180  
     181  class known_function_copy_from_user : public copy_across_boundary_fn
     182  {
     183  public:
     184    bool untrusted_source_p () const final override
     185    {
     186      return true;
     187    }
     188    bool untrusted_destination_p () const final override
     189    {
     190      return false;
     191    }
     192  };
     193  
     194  /* "copy_to_user".  */
     195  
     196  class known_function_copy_to_user : public copy_across_boundary_fn
     197  {
     198  public:
     199    bool untrusted_source_p () const final override
     200    {
     201      return false;
     202    }
     203    bool untrusted_destination_p () const final override
     204    {
     205      return true;
     206    }
     207  };
     208  
     209  /* Callback handler for the PLUGIN_ANALYZER_INIT event.  */
     210  
     211  static void
     212  kernel_analyzer_init_cb (void *gcc_data, void */*user_data*/)
     213  {
     214    ana::plugin_analyzer_init_iface *iface
     215      = (ana::plugin_analyzer_init_iface *)gcc_data;
     216    LOG_SCOPE (iface->get_logger ());
     217    if (0)
     218      inform (input_location, "got here: kernel_analyzer_init_cb");
     219    iface->register_known_function
     220      ("copy_from_user",
     221       make_unique<known_function_copy_from_user> ());
     222    iface->register_known_function ("copy_to_user",
     223  				  make_unique<known_function_copy_to_user> ());
     224  }
     225  
     226  } // namespace ana
     227  
     228  #endif /* #if ENABLE_ANALYZER */
     229  
     230  int
     231  plugin_init (struct plugin_name_args *plugin_info,
     232  	     struct plugin_gcc_version *version)
     233  {
     234  #if ENABLE_ANALYZER
     235    const char *plugin_name = plugin_info->base_name;
     236    if (0)
     237      inform (input_location, "got here; %qs", plugin_name);
     238    register_callback (plugin_info->base_name,
     239  		     PLUGIN_ANALYZER_INIT,
     240  		     ana::kernel_analyzer_init_cb,
     241  		     NULL); /* void *user_data */
     242  #else
     243    sorry_no_analyzer ();
     244  #endif
     245    return 0;
     246  }