1  /* { dg-options "-O" } */
       2  
       3  /* This plugin exercises the path-printing code.
       4  
       5     The goal is to unit-test the path-printing code without needing any
       6     specific tests within the compiler's IR.  We can't use any real
       7     diagnostics for this, so we have to fake it, hence this plugin.  */
       8  
       9  #include "gcc-plugin.h"
      10  #include "config.h"
      11  #include "system.h"
      12  #include "coretypes.h"
      13  #include "tm.h"
      14  #include "tree.h"
      15  #include "stringpool.h"
      16  #include "toplev.h"
      17  #include "basic-block.h"
      18  #include "hash-table.h"
      19  #include "vec.h"
      20  #include "ggc.h"
      21  #include "basic-block.h"
      22  #include "tree-ssa-alias.h"
      23  #include "internal-fn.h"
      24  #include "gimple.h"
      25  #include "gimple-iterator.h"
      26  #include "gimple-fold.h"
      27  #include "tree-eh.h"
      28  #include "gimple-expr.h"
      29  #include "is-a.h"
      30  #include "tree.h"
      31  #include "tree-pass.h"
      32  #include "intl.h"
      33  #include "plugin-version.h"
      34  #include "diagnostic.h"
      35  #include "diagnostic-path.h"
      36  #include "diagnostic-metadata.h"
      37  #include "context.h"
      38  #include "print-tree.h"
      39  #include "gcc-rich-location.h"
      40  #include "cgraph.h"
      41  
      42  int plugin_is_GPL_compatible;
      43  
      44  const pass_data pass_data_test_show_path =
      45  {
      46    IPA_PASS, /* type */
      47    "test_show_path", /* name */
      48    OPTGROUP_NONE, /* optinfo_flags */
      49    TV_NONE, /* tv_id */
      50    PROP_ssa, /* properties_required */
      51    0, /* properties_provided */
      52    0, /* properties_destroyed */
      53    0, /* todo_flags_start */
      54    0, /* todo_flags_finish */
      55  };
      56  
      57  class pass_test_show_path : public ipa_opt_pass_d
      58  {
      59  public:
      60    pass_test_show_path(gcc::context *ctxt)
      61      : ipa_opt_pass_d (pass_data_test_show_path, ctxt,
      62  		      NULL, /* generate_summary */
      63  		      NULL, /* write_summary */
      64  		      NULL, /* read_summary */
      65  		      NULL, /* write_optimization_summary */
      66  		      NULL, /* read_optimization_summary */
      67  		      NULL, /* stmt_fixup */
      68  		      0, /* function_transform_todo_flags_start */
      69  		      NULL, /* function_transform */
      70  		      NULL) /* variable_transform */
      71    {}
      72  
      73    /* opt_pass methods: */
      74    bool gate (function *) { return true; }
      75    virtual unsigned int execute (function *);
      76  
      77  }; // class pass_test_show_path
      78  
      79  /* Determine if STMT is a call with NUM_ARGS arguments to a function
      80     named FUNCNAME.
      81     If so, return STMT as a gcall *.  Otherwise return NULL.  */
      82  
      83  static gcall *
      84  check_for_named_call (gimple *stmt,
      85  		      const char *funcname, unsigned int num_args)
      86  {
      87    gcc_assert (funcname);
      88  
      89    gcall *call = dyn_cast <gcall *> (stmt);
      90    if (!call)
      91      return NULL;
      92  
      93    tree fndecl = gimple_call_fndecl (call);
      94    if (!fndecl)
      95      return NULL;
      96  
      97    if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
      98      return NULL;
      99  
     100    if (gimple_call_num_args (call) != num_args)
     101      {
     102        error_at (stmt->location, "expected number of args: %i (got %i)",
     103  		num_args, gimple_call_num_args (call));
     104        return NULL;
     105      }
     106  
     107    return call;
     108  }
     109  
     110  /* Example 1: a purely intraprocedural path.  */
     111  
     112  static void
     113  example_1 ()
     114  {
     115    gimple_stmt_iterator gsi;
     116    basic_block bb;
     117  
     118    gcall *call_to_PyList_Append = NULL;
     119    gcall *call_to_PyList_New = NULL;
     120    gcond *for_cond = NULL;
     121    function *example_a_fun = NULL;
     122  
     123    cgraph_node *node;
     124    FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
     125      {
     126        function *fun = node->get_fun ();
     127        FOR_EACH_BB_FN (bb, fun)
     128  	{
     129  	  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     130  	    {
     131  	      gimple *stmt = gsi_stmt (gsi);
     132  	      if (gcall *call = check_for_named_call (stmt, "PyList_New", 1))
     133  		{
     134  		  call_to_PyList_New = call;
     135  		  example_a_fun = fun;
     136  		}
     137  	      if (gcall *call = check_for_named_call (stmt, "PyList_Append", 2))
     138  		call_to_PyList_Append = call;
     139  	      if (gcond *cond = dyn_cast <gcond *> (stmt))
     140  		for_cond = cond;
     141  	    }
     142  	}
     143      }
     144  
     145    if (call_to_PyList_New && for_cond && call_to_PyList_Append)
     146      {
     147        auto_diagnostic_group d;
     148        gcc_rich_location richloc (gimple_location (call_to_PyList_Append));
     149        simple_diagnostic_path path (global_dc->printer);
     150        diagnostic_event_id_t alloc_event_id
     151  	= path.add_event (gimple_location (call_to_PyList_New),
     152  			  example_a_fun->decl, 0,
     153  			  "when %qs fails, returning NULL",
     154  			  "PyList_New");
     155        path.add_event (gimple_location (for_cond),
     156  		      example_a_fun->decl, 0,
     157  		      "when %qs", "i < count");
     158        path.add_event (gimple_location (call_to_PyList_Append),
     159  		      example_a_fun->decl, 0,
     160  		      "when calling %qs, passing NULL from %@ as argument %i",
     161  		      "PyList_Append", &alloc_event_id, 1);
     162        richloc.set_path (&path);
     163        error_at (&richloc,
     164  		"passing NULL as argument %i to %qs"
     165  		" which requires a non-NULL parameter",
     166  		1, "PyList_Append");
     167      }
     168  }
     169  
     170  /* A (function, location_t) pair.  */
     171  
     172  struct event_location_t
     173  {
     174    event_location_t ()
     175    : m_fun (NULL), m_loc (UNKNOWN_LOCATION)
     176    {}
     177  
     178    event_location_t (function *fun, location_t loc)
     179    : m_fun (fun), m_loc (loc)
     180    {}
     181  
     182    void set (const gimple *stmt, function *fun)
     183    {
     184      m_fun = fun;
     185      m_loc = gimple_location (stmt);
     186    }
     187  
     188    function *m_fun;
     189    location_t m_loc;
     190  };
     191  
     192  /* If FUN's name matches FUNCNAME, write the function and its start location
     193     into *OUT_ENTRY.  */
     194  
     195  static void
     196  check_for_named_function (function *fun, const char *funcname,
     197  			  event_location_t *out_entry)
     198  {
     199    gcc_assert (fun);
     200    gcc_assert (funcname);
     201  
     202    if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fun->decl)), funcname))
     203      return;
     204  
     205    *out_entry = event_location_t (fun, fun->function_start_locus);
     206  }
     207  
     208  
     209  /* Example 2: an interprocedural path.  */
     210  
     211  class test_diagnostic_path : public simple_diagnostic_path
     212  {
     213   public:
     214    test_diagnostic_path (pretty_printer *event_pp)
     215    : simple_diagnostic_path (event_pp)
     216    {
     217    }
     218    void add_entry (event_location_t evloc, int stack_depth,
     219  		  const char *funcname)
     220    {
     221      gcc_assert (evloc.m_fun);
     222      add_event (evloc.m_loc, evloc.m_fun->decl, stack_depth,
     223  	       "entering %qs", funcname);
     224    }
     225  
     226    void add_call (event_location_t call_evloc, int caller_stack_depth,
     227  		 event_location_t callee_entry_evloc, const char *callee)
     228    {
     229      gcc_assert (call_evloc.m_fun);
     230      add_event (call_evloc.m_loc, call_evloc.m_fun->decl, caller_stack_depth,
     231  	       "calling %qs", callee);
     232      add_entry (callee_entry_evloc, caller_stack_depth + 1, callee);
     233    }
     234  
     235    void add_leaf_call (event_location_t call_evloc, int caller_stack_depth,
     236  		      const char *callee)
     237    {
     238      gcc_assert (call_evloc.m_fun);
     239      add_event (call_evloc.m_loc, call_evloc.m_fun->decl, caller_stack_depth,
     240  	       "calling %qs", callee);
     241    }
     242  };
     243  
     244  static void
     245  example_2 ()
     246  {
     247    gimple_stmt_iterator gsi;
     248    basic_block bb;
     249  
     250    event_location_t entry_to_wrapped_malloc;
     251    event_location_t call_to_malloc;
     252  
     253    event_location_t entry_to_wrapped_free;
     254    event_location_t call_to_free;
     255  
     256    event_location_t entry_to_make_boxed_int;
     257    event_location_t call_to_wrapped_malloc;
     258  
     259    event_location_t entry_to_free_boxed_int;
     260    event_location_t call_to_wrapped_free;
     261  
     262    event_location_t entry_to_test;
     263    event_location_t call_to_make_boxed_int;
     264    event_location_t call_to_free_boxed_int;
     265  
     266    event_location_t call_to_missing_location;
     267  
     268    cgraph_node *node;
     269    FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
     270      {
     271        function *fun = node->get_fun ();
     272        FOR_EACH_BB_FN (bb, fun)
     273  	{
     274  	  check_for_named_function (fun, "wrapped_malloc",
     275  				    &entry_to_wrapped_malloc);
     276  	  check_for_named_function (fun, "wrapped_free",
     277  				    &entry_to_wrapped_free);
     278  	  check_for_named_function (fun, "make_boxed_int",
     279  				    &entry_to_make_boxed_int);
     280  	  check_for_named_function (fun, "free_boxed_int",
     281  				    &entry_to_free_boxed_int);
     282  	  check_for_named_function (fun, "test",
     283  				    &entry_to_test);
     284  
     285  	  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     286  	    {
     287  	      gimple *stmt = gsi_stmt (gsi);
     288  	      if (gcall *call = check_for_named_call (stmt, "malloc", 1))
     289  		call_to_malloc.set (call, fun);
     290  	      if (gcall *call = check_for_named_call (stmt, "free", 1))
     291  		call_to_free.set (call, fun);
     292  	      if (gcall *call = check_for_named_call (stmt, "wrapped_malloc", 1))
     293  		call_to_wrapped_malloc.set (call, fun);
     294  	      if (gcall *call = check_for_named_call (stmt, "wrapped_free", 1))
     295  		call_to_wrapped_free.set (call, fun);
     296  	      if (gcall *call = check_for_named_call (stmt, "make_boxed_int", 1))
     297  		call_to_make_boxed_int.set (call, fun);
     298  	      if (gcall *call = check_for_named_call (stmt, "free_boxed_int", 1))
     299  		call_to_free_boxed_int.set (call, fun);
     300  	      if (gcall *call = check_for_named_call (stmt, "missing_location", 0))
     301  		{
     302  		  call_to_missing_location.set (call, fun);
     303  		  /* Simulate an event that's missing a useful location_t.  */
     304  		  call_to_missing_location.m_loc = UNKNOWN_LOCATION;
     305  		}
     306  	    }
     307  	}
     308      }
     309  
     310    if (call_to_malloc.m_fun)
     311      {
     312        auto_diagnostic_group d;
     313  
     314        gcc_rich_location richloc (call_to_free.m_loc);
     315        test_diagnostic_path path (global_dc->printer);
     316        path.add_entry (entry_to_test, 0, "test");
     317        path.add_call (call_to_make_boxed_int, 0,
     318  		     entry_to_make_boxed_int, "make_boxed_int");
     319        path.add_call (call_to_wrapped_malloc, 1,
     320  		     entry_to_wrapped_malloc, "wrapped_malloc");
     321        path.add_leaf_call (call_to_malloc, 2, "malloc");
     322  
     323        for (int i = 0; i < 2; i++)
     324  	{
     325  	  path.add_call (call_to_free_boxed_int, 0,
     326  			 entry_to_free_boxed_int, "free_boxed_int");
     327  	  path.add_call (call_to_wrapped_free, 1,
     328  			 entry_to_wrapped_free, "wrapped_free");
     329  	  path.add_leaf_call (call_to_free, 2, "free");
     330  	  if (i == 0 && call_to_missing_location.m_fun)
     331  	    path.add_leaf_call (call_to_missing_location, 0,
     332  				"missing_location");
     333  	}
     334  
     335        richloc.set_path (&path);
     336  
     337        diagnostic_metadata m;
     338        m.add_cwe (415); /* CWE-415: Double Free.  */
     339  
     340        warning_meta (&richloc, m, 0,
     341  		    "double-free of %qs", "ptr");
     342      }
     343  }
     344  
     345  /* Example 3: an interprocedural path with a callback.  */
     346  
     347  static void
     348  example_3 ()
     349  {
     350    gimple_stmt_iterator gsi;
     351    basic_block bb;
     352  
     353    event_location_t entry_to_custom_logger;
     354    event_location_t call_to_fprintf;
     355  
     356    event_location_t entry_to_int_handler;
     357    event_location_t call_to_custom_logger;
     358  
     359    event_location_t entry_to_register_handler;
     360    event_location_t call_to_signal;
     361  
     362    event_location_t entry_to_test;
     363    event_location_t call_to_register_handler;
     364  
     365    cgraph_node *node;
     366    FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
     367      {
     368        function *fun = node->get_fun ();
     369        FOR_EACH_BB_FN (bb, fun)
     370  	{
     371  	  check_for_named_function (fun, "custom_logger",
     372  				    &entry_to_custom_logger);
     373  	  check_for_named_function (fun, "int_handler",
     374  				    &entry_to_int_handler);
     375  	  check_for_named_function (fun, "register_handler",
     376  				    &entry_to_register_handler);
     377  	  check_for_named_function (fun, "test",
     378  				    &entry_to_test);
     379  	  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     380  	    {
     381  	      gimple *stmt = gsi_stmt (gsi);
     382  	      if (gcall *call = check_for_named_call (stmt, "fprintf", 3))
     383  		call_to_fprintf.set (call, fun);
     384  	      if (gcall *call = check_for_named_call (stmt, "custom_logger", 1))
     385  		call_to_custom_logger.set (call, fun);
     386  	      if (gcall *call = check_for_named_call (stmt, "register_handler",
     387  						      0))
     388  		call_to_register_handler.set (call, fun);
     389  	      if (gcall *call = check_for_named_call (stmt, "signal", 2))
     390  		call_to_signal.set (call, fun);
     391  	    }
     392  	}
     393      }
     394  
     395    if (call_to_fprintf.m_fun)
     396      {
     397        auto_diagnostic_group d;
     398  
     399        gcc_rich_location richloc (call_to_fprintf.m_loc);
     400        test_diagnostic_path path (global_dc->printer);
     401        path.add_entry (entry_to_test, 1, "test");
     402        path.add_call (call_to_register_handler, 1,
     403  		     entry_to_register_handler, "register_handler");
     404        path.add_event (call_to_signal.m_loc, call_to_signal.m_fun->decl,
     405  		      2, "registering 'int_handler' as signal handler");
     406        path.add_event (UNKNOWN_LOCATION, NULL_TREE, 0,
     407  		      "later on, when the signal is delivered to the process");
     408        path.add_entry (entry_to_int_handler, 1, "int_handler");
     409        path.add_call (call_to_custom_logger, 1,
     410  		     entry_to_custom_logger, "custom_logger");
     411        path.add_leaf_call (call_to_fprintf, 2, "fprintf");
     412  
     413        richloc.set_path (&path);
     414  
     415        diagnostic_metadata m;
     416        /* CWE-479: Signal Handler Use of a Non-reentrant Function.  */
     417        m.add_cwe (479);
     418  
     419        warning_meta (&richloc, m, 0,
     420  		    "call to %qs from within signal handler",
     421  		    "fprintf");
     422      }
     423  }
     424  
     425  unsigned int
     426  pass_test_show_path::execute (function *)
     427  {
     428    example_1 ();
     429    example_2 ();
     430    example_3 ();
     431  
     432    return 0;
     433  }
     434  
     435  static opt_pass *
     436  make_pass_test_show_path (gcc::context *ctxt)
     437  {
     438    return new pass_test_show_path (ctxt);
     439  }
     440  
     441  int
     442  plugin_init (struct plugin_name_args *plugin_info,
     443  	     struct plugin_gcc_version *version)
     444  {
     445    struct register_pass_info pass_info;
     446    const char *plugin_name = plugin_info->base_name;
     447    int argc = plugin_info->argc;
     448    struct plugin_argument *argv = plugin_info->argv;
     449  
     450    if (!plugin_default_version_check (version, &gcc_version))
     451      return 1;
     452  
     453    global_dc->caret_max_width = 80;
     454  
     455    pass_info.pass = make_pass_test_show_path (g);
     456    pass_info.reference_pass_name = "whole-program";
     457    pass_info.ref_pass_instance_number = 1;
     458    pass_info.pos_op = PASS_POS_INSERT_BEFORE;
     459    register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
     460  		     &pass_info);
     461  
     462    return 0;
     463  }