From 8d37652979c9eb35fce799b5c794a68651c91482 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 26 Mar 2019 12:56:35 -0400
Subject: [PATCH 148/217] FIXME: fix engine::prune_path to track var through
 calls/returns

---
 gcc/analyzer/engine.cc                       | 32 ++++++++++++++++++++++++++--
 gcc/analyzer/supergraph.cc                   | 20 +++++++++++++++--
 gcc/analyzer/supergraph.h                    |  4 ++--
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8.c |  4 +++-
 4 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index b669769..5085123 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -794,6 +794,12 @@ public:
       }
   }
 
+  const call_superedge& get_call_superedge () const
+  {
+    gcc_assert (m_edge->m_kind != SUPEREDGE_CFG_EDGE);
+    return *m_edge->dyn_cast_call_superedge ();
+  }
+
   checker_event *clone () const FINAL OVERRIDE
   {
     return new edge_event (m_edge, m_loc, m_fndecl, m_depth);
@@ -3005,7 +3011,7 @@ engine::expand_path (const checker_path &in_path,
 	    /* Attempt to convert "var" to the callee scope.  */
 	    call_superedge *call_edge
 	      = summary_event->get_edge_for_call (&m_sg);
-	    tree callee_var = call_edge->refine_for_call (var);
+	    tree callee_var = call_edge->map_expr_from_caller_to_callee (var);
 	    if (callee_var == NULL_TREE)
 	      {
 		log ("event: %i"
@@ -3067,6 +3073,9 @@ engine::prune_path (checker_path *path,
   while (idx >= 0 && idx < path->m_events.length ())
     {
       checker_event *base_event = path->m_events[idx];
+      if (get_logger ())
+	log  ("considering event %i, with var: %qE, state: %qs",
+	      idx, var, m_sm.get_state_name (state));
       switch (base_event->m_kind)
 	{
 	default:
@@ -3140,9 +3149,28 @@ engine::prune_path (checker_path *path,
 		break;
 
 	      case SUPEREDGE_CALL:
+		{
+		  const call_superedge& call_superedge
+		    = event->get_call_superedge ();
+		  tree caller_var
+		    = call_superedge.map_expr_from_callee_to_caller (var);
+		  if (caller_var)
+		    var = caller_var;
+		}
+		break;
 	      case SUPEREDGE_RETURN:
+		// TODO: potentially update var/state based on return value,
+		// args etc
+		{
+		  const call_superedge& call_superedge
+		    = event->get_call_superedge ();
+		  tree callee_var
+		    = call_superedge.map_expr_from_caller_to_callee (var);
+		  if (callee_var)
+		    var = callee_var;
+		}
+		break;
 	      case SUPEREDGE_INTRAPROCEDURAL_CALL:
-		// TODO
 		break;
 	      }
 	  }
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index 49012e0..1596c7c 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -808,10 +808,10 @@ call_superedge::get_parm_for_arg (tree arg_to_find) const
   return NULL_TREE;
 }
 
-/* FIXME.  */
+/* Map caller_expr back to an expr within the callee, or return NULL_TREE.  */
 
 tree
-call_superedge::refine_for_call (tree caller_expr) const
+call_superedge::map_expr_from_caller_to_callee (tree caller_expr) const
 {
   /* Is it an argument (actual param)?  If so, convert to
      parameter (formal param).  */
@@ -824,3 +824,19 @@ call_superedge::refine_for_call (tree caller_expr) const
 
   return NULL_TREE;
 }
+
+/* Map callee_expr back to an expr within the caller, or return NULL_TREE.  */
+
+tree
+call_superedge::map_expr_from_callee_to_caller (tree callee_expr) const
+{
+  /* If it's a parameter (formal param), get the argument (actual param).  */
+  if (TREE_CODE (callee_expr) == PARM_DECL)
+    return get_arg_for_parm (callee_expr);
+
+  /* Otherwise try return value.  */
+  if (callee_expr == DECL_RESULT (get_callee_decl ()))
+    return gimple_call_lhs (get_call_stmt ());
+
+  return NULL_TREE;
+}
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index a3dad4c..782543a 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -276,8 +276,8 @@ class call_superedge : public superedge
   gcall *get_call_stmt () const { return m_cedge->call_stmt; }
   tree get_arg_for_parm (tree parm) const;
   tree get_parm_for_arg (tree arg) const;
-
-  tree refine_for_call (tree caller_expr) const;
+  tree map_expr_from_caller_to_callee (tree caller_expr) const;
+  tree map_expr_from_callee_to_caller (tree callee_expr) const;
 
   cgraph_edge *m_cedge;
 };
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8.c
index c9034f1..d630f13 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8.c
@@ -1,4 +1,6 @@
 /* Example of a multilevel wrapper around malloc/free, with a double-free.  */
+/* TODO: split into two test cases?
+   (one for the unchecked malloc, the other for the double-free).  */
 
 /* { dg-options "--analyzer -fdiagnostics-path-summary-format=none -fdump-analyzer-summaries -fanalyzer-checker=malloc" } */
 
@@ -7,7 +9,7 @@
 void *wrapped_malloc (size_t size) /* { dg-warning "summary: add: '<return-value>': 'start' -> 'unchecked'" } */
 /* { dg-message "\\(4\\) entry to 'wrapped_malloc'" "" { target *-*-* } .-1 } */
 {
-  return malloc (size);
+  return malloc (size); /* { dg-message "this call could return NULL" } */
 }
 
 void wrapped_free (void *ptr) /* { dg-warning "summary: add: 'ptr': 'start' -> 'free'" } */
-- 
1.8.5.3

