From a220f7c8da2a7640b421731f4939a52556cb14db Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 9 May 2019 18:01:47 -0400
Subject: [PATCH 29/54] FIXME: purge sm state for unrecognized function calls

---
 gcc/analyzer/hardcoded-state-machine-file.cc       |   5 +-
 gcc/analyzer/hardcoded-state-machine-malloc.cc     |   6 +-
 .../hardcoded-state-machine-pattern-test.cc        |   3 +-
 gcc/analyzer/hardcoded-state-machine-sensitive.cc  |   6 +-
 gcc/analyzer/hardcoded-state-machine-taint.cc      |   4 +-
 gcc/analyzer/hardcoded-state-machine.h             |  13 +--
 gcc/analyzer/impl-4.cc                             | 112 ++++++++++++++++++---
 gcc/testsuite/gcc.dg/analyzer/malloc-1.c           |  12 ++-
 gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c     |   2 +-
 9 files changed, 133 insertions(+), 30 deletions(-)

diff --git a/gcc/analyzer/hardcoded-state-machine-file.cc b/gcc/analyzer/hardcoded-state-machine-file.cc
index 8d74298..9cc3cb5 100644
--- a/gcc/analyzer/hardcoded-state-machine-file.cc
+++ b/gcc/analyzer/hardcoded-state-machine-file.cc
@@ -88,7 +88,7 @@ fileptr_state_machine::fileptr_state_machine (logger *logger)
   m_stop = add_state ("stop");
 }
 
-void
+bool
 fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 				const supernode *node,
 				const gimple *stmt) const
@@ -109,6 +109,7 @@ fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 	    {
 	      /* TODO: report leak.  */
 	    }
+	  return true;
 	}
 
       if (is_named_call_p (call, "fclose", 1))
@@ -127,11 +128,13 @@ fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 	  sm_ctxt->warn_for_state (node, stmt, arg, m_closed,
 				   new double_fclose (arg));
 	  sm_ctxt->on_transition (node, stmt, arg, m_closed, m_stop);
+	  return true;
 	}
 
       // TODO: operations on closed file
       // etc
     }
+  return false;
 }
 
 /* FIXME.  */
diff --git a/gcc/analyzer/hardcoded-state-machine-malloc.cc b/gcc/analyzer/hardcoded-state-machine-malloc.cc
index cf61b0d..bb0d16d 100644
--- a/gcc/analyzer/hardcoded-state-machine-malloc.cc
+++ b/gcc/analyzer/hardcoded-state-machine-malloc.cc
@@ -323,7 +323,7 @@ malloc_state_machine::malloc_state_machine (logger *logger)
   m_stop = add_state ("stop");
 }
 
-void
+bool
 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 			       const supernode *node,
 			       const gimple *stmt) const
@@ -345,6 +345,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	    {
 	      /* TODO: report leak.  */
 	    }
+	  return true;
 	}
 
       if (is_named_call_p (call, "alloca", 1)
@@ -353,6 +354,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	  tree lhs = gimple_call_lhs (call);
 	  if (lhs)
 	    sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
+	  return true;
 	}
 
       if (is_named_call_p (call, "free", 1))
@@ -378,6 +380,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	  sm_ctxt->warn_for_state (node, stmt, arg, m_non_heap,
 				   new free_of_non_heap (*this, arg));
 	  sm_ctxt->on_transition (node, stmt, arg, m_non_heap, m_stop);
+	  return true;
 	}
     }
 
@@ -437,6 +440,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	  sm_ctxt->on_transition (node, stmt, arg, m_free, m_stop);
 	}
     }
+  return false;
 }
 
 /* FIXME.  */
diff --git a/gcc/analyzer/hardcoded-state-machine-pattern-test.cc b/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
index 7f1460c..b53872c 100644
--- a/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
+++ b/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
@@ -65,7 +65,7 @@ pattern_test_state_machine::pattern_test_state_machine (logger *logger)
   m_start = add_state ("start");
 }
 
-void
+bool
 pattern_test_state_machine::on_stmt (sm_context *sm_ctxt,
 				     const supernode *node,
 				     const gimple *stmt) const
@@ -76,6 +76,7 @@ pattern_test_state_machine::on_stmt (sm_context *sm_ctxt,
   if (const gcall *call = dyn_cast <const gcall *> (stmt))
     {
     }
+  return false;
 }
 
 /* FIXME.  */
diff --git a/gcc/analyzer/hardcoded-state-machine-sensitive.cc b/gcc/analyzer/hardcoded-state-machine-sensitive.cc
index 59e5cb7..1e89cfe 100644
--- a/gcc/analyzer/hardcoded-state-machine-sensitive.cc
+++ b/gcc/analyzer/hardcoded-state-machine-sensitive.cc
@@ -80,7 +80,7 @@ sensitive_state_machine::warn_for_any_leakage (sm_context *sm_ctxt,
 			   new leakage_through_output_file (arg));
 }
 
-void
+bool
 sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
 				  const supernode *node,
 				  const gimple *stmt) const
@@ -95,6 +95,7 @@ sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
 	  tree lhs = gimple_call_lhs (call);
 	  if (lhs)
 	    sm_ctxt->on_transition (node, stmt, lhs, m_start, m_sensitive);
+	  return true;
 	}
       else if (is_named_call_p (call, "fprintf")
 	       || is_named_call_p (call, "printf"))
@@ -105,13 +106,16 @@ sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
 	      tree arg = gimple_call_arg (call, idx);
 	      warn_for_any_leakage (sm_ctxt, node, stmt, arg);
 	    }
+	  return true;
 	}
       else if (is_named_call_p (call, "fwrite", 4))
 	{
 	  tree arg = gimple_call_arg (call, 0);
 	  warn_for_any_leakage (sm_ctxt, node, stmt, arg);
+	  return true;
 	}
     }
+  return false;
 }
 
 /* FIXME.  */
diff --git a/gcc/analyzer/hardcoded-state-machine-taint.cc b/gcc/analyzer/hardcoded-state-machine-taint.cc
index 682726c..9d39d24 100644
--- a/gcc/analyzer/hardcoded-state-machine-taint.cc
+++ b/gcc/analyzer/hardcoded-state-machine-taint.cc
@@ -71,7 +71,7 @@ taint_state_machine::taint_state_machine (logger *logger)
   m_stop = add_state ("stop");
 }
 
-void
+bool
 taint_state_machine::on_stmt (sm_context *sm_ctxt,
 			       const supernode *node,
 			       const gimple *stmt) const
@@ -93,6 +93,7 @@ taint_state_machine::on_stmt (sm_context *sm_ctxt,
 	  if (TREE_CODE (arg) == ADDR_EXPR)
 	    sm_ctxt->on_transition (node, stmt, TREE_OPERAND (arg, 0),
 				    m_start, m_tainted);
+	  return true;
 	}
     }
 
@@ -155,6 +156,7 @@ taint_state_machine::on_stmt (sm_context *sm_ctxt,
 	}
     }
 #endif
+  return false;
 }
 
 /* FIXME.  */
diff --git a/gcc/analyzer/hardcoded-state-machine.h b/gcc/analyzer/hardcoded-state-machine.h
index 274c968..81f5084 100644
--- a/gcc/analyzer/hardcoded-state-machine.h
+++ b/gcc/analyzer/hardcoded-state-machine.h
@@ -54,7 +54,8 @@ public:
 
   const char *get_state_name (state_t s) const;
 
-  virtual void on_stmt (sm_context *sm_ctxt,
+  /* Return true if STMT is a function call recognized by this sm.  */
+  virtual bool on_stmt (sm_context *sm_ctxt,
 			const supernode *node,
 			const gimple *stmt) const = 0;
 
@@ -226,7 +227,7 @@ class malloc_state_machine : public state_machine
 public:
   malloc_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -260,7 +261,7 @@ class fileptr_state_machine : public state_machine
 public:
   fileptr_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -293,7 +294,7 @@ class taint_state_machine : public state_machine
 public:
   taint_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -325,7 +326,7 @@ class sensitive_state_machine : public state_machine
 public:
   sensitive_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -360,7 +361,7 @@ class pattern_test_state_machine : public state_machine
 public:
   pattern_test_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 1ab9c45..a7fd4b0 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -634,6 +634,11 @@ public:
 
   void purge_stack_frame (int callee_stack_depth);
 
+  void purge_for_unknown_fncall (const exploded_graph &eg,
+				 const state_machine &sm,
+				 const gcall *call, tree fndecl,
+				 path_state *new_ps);
+
   iterator_t begin () const { return m_map.begin (); }
   iterator_t end () const { return m_map.end (); }
 
@@ -1929,6 +1934,53 @@ sm_state_map::purge_stack_frame (int callee_stack_depth)
       m_map.remove (*pv);
 }
 
+/* FIXME.  */
+
+void
+sm_state_map::purge_for_unknown_fncall (const exploded_graph &eg,
+					const state_machine &sm,
+					const gcall *call, tree fndecl,
+					path_state *new_ps)
+{
+  if (fndecl)
+    eg.log ("function %qE is unknown to checker %qs",
+	    fndecl, sm.get_name ());
+  else
+    eg.log ("unknown function pointer for checker %qs",
+	    sm.get_name ());
+
+  /* Unknown function body, which could do anything to the
+     states passed to it.
+     Clear any state for the params and any LHS.  */
+  tree iter_param_types = NULL_TREE;
+  if (fndecl)
+    iter_param_types = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  for (int arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
+    {
+      /* Track expected param type, where available.  */
+      if (iter_param_types)
+	{
+	  tree param_type = TREE_VALUE (iter_param_types);
+	  gcc_assert (param_type);
+	  iter_param_types = TREE_CHAIN (iter_param_types);
+
+	  /* Don't purge state if it was passed as a const pointer
+	     e.g. for things like strlen (PTR).  */
+	  if (TREE_CODE (param_type) == POINTER_TYPE)
+	    if (TYPE_READONLY (TREE_TYPE (param_type)))
+	      continue;
+	}
+
+      set_state (new_ps,
+		 new_ps->make_path_var (gimple_call_arg (call, arg_idx)),
+		 0, path_var (NULL_TREE, 0));
+    }
+
+  if (gimple_call_lhs (call))
+    set_state (new_ps,
+	       new_ps->make_path_var (gimple_call_lhs (call)),
+	       0, path_var (NULL_TREE, 0));
+}
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -2359,6 +2411,22 @@ exploded_node::dump_dot_id (pretty_printer *pp) const
 }
 
 /* FIXME.  */
+// is there a pre-canned way to do this?
+
+static bool
+fndecl_has_gimple_body_p (tree fndecl)
+{
+  if (fndecl == NULL_TREE)
+    return false;
+
+  cgraph_node *n = cgraph_node::get (fndecl);
+  if (!n)
+    return false;
+
+  return n->has_gimple_body_p ();
+}
+
+/* FIXME.  */
 
 program_state
 exploded_node::on_stmt (const exploded_graph &eg) const
@@ -2373,16 +2441,30 @@ exploded_node::on_stmt (const exploded_graph &eg) const
   const gimple *stmt = point.get_stmt ();
   int stack_depth = point.get_stack_depth ();
 
-  int i;
+  int sm_idx;
   sm_state_map *smap;
-  FOR_EACH_VEC_ELT (old_state.m_checker_states, i, smap)
+  FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
     {
-      const state_machine &sm = m_ext_state.get_sm (i);
+      const state_machine &sm = m_ext_state.get_sm (sm_idx);
+      const sm_state_map &old_smap = *old_state.m_checker_states[sm_idx];
+      sm_state_map *new_smap = new_state.m_checker_states[sm_idx];
       impl_sm_context ctxt (eg, sm, this,
 			    &new_state,
-			    *old_state.m_checker_states[i],
-			    new_state.m_checker_states[i]);
-      sm.on_stmt (&ctxt, point.m_supernode, stmt);
+			    old_smap, new_smap);
+      /* Allow the state_machine to handle the stmt.  */
+      if (!sm.on_stmt (&ctxt, point.m_supernode, stmt))
+	{
+	  /* For those stmts that were not handled by the state machine.  */
+	  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+	    {
+	      tree callee_fndecl = gimple_call_fndecl (call);
+	      // TODO: maybe we can be smarter about handling function pointers?
+
+	      if (!fndecl_has_gimple_body_p (callee_fndecl))
+		new_smap->purge_for_unknown_fncall (eg, sm, call, callee_fndecl,
+						    new_ps);
+	    }
+	}
     }
 
   if (const gassign *assign = dyn_cast <const gassign *> (stmt))
@@ -2464,15 +2546,16 @@ exploded_node::on_stmt (const exploded_graph &eg) const
     {
       /* Debugging/test support.  */
       if (is_named_call_p (call, "__dump_constraints", 0))
-	inform (stmt->location, "__dump_constraints: %s",
-		new_ps->to_str ().c_str ());
-
-      /* Handle the builtin "__analyzer_eval" by evaluating the input
-	 and dumping as a dummy warning, so that test cases can use dg-warning
-	 to validate the result (and so unexpected warnings will lead to DejaGnu
-	 failures.  */
-      if (is_named_call_p (call, "__analyzer_eval", 1))
 	{
+	  inform (stmt->location, "__dump_constraints: %s",
+		  new_ps->to_str ().c_str ());
+	}
+      else if (is_named_call_p (call, "__analyzer_eval", 1))
+	{
+	  /* Handle the builtin "__analyzer_eval" by evaluating the input
+	     and dumping as a dummy warning, so that test cases can use dg-warning
+	     to validate the result (and so unexpected warnings will lead to DejaGnu
+	     failures.  */
 	  tree t_arg = gimple_call_arg (call, 0);
 	  tristate t
 	    = new_ps->dump_condition (t_arg, NE_EXPR, integer_zero_node);
@@ -2480,7 +2563,6 @@ exploded_node::on_stmt (const exploded_graph &eg) const
 	}
     }
 
-
   return new_state;
 }
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
index a3796b6..cb95f68 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
@@ -4,6 +4,7 @@
 extern int foo (void);
 extern int bar (void);
 extern void could_free (void *);
+extern void cant_free (const void *); /* since it's a const void *.  */
 
 void test_1 (void)
 {
@@ -350,10 +351,15 @@ void test_31 (void)
 
 void test_32 (void)
 {
-  void *ptr = malloc (sizeof (struct link));
+  void *ptr = malloc (1024);
   could_free (ptr);
-} /* { dg-bogus "leak" "" { xfail *-*-* } } */
-/* TODO(xfail) */
+} /* { dg-bogus "leak" } */
+
+void test_33 (void)
+{
+  void *ptr = malloc (1024);
+  cant_free (ptr);
+} /* { dg-warning "leak of 'ptr'" } */ 
 
 #if 0
 void test_31 (void *p)
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
index d033969..162799b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
@@ -1,7 +1,7 @@
 #include <stddef.h>
 #include <stdlib.h>
 
-extern void do_stuff (void *);
+extern void do_stuff (const void *);
 
 #define LIMIT 1024
 
-- 
1.8.5.3

