From e44d3677e2b43f4008f7a0236b2c2e0c1095c137 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Sat, 23 Nov 2019 18:44:43 -0500
Subject: [PATCH 93/95] FIXME: known-functions experiment

---
 gcc/analyzer/Make-plugin.in      |  1 +
 gcc/analyzer/known-functions.cc  | 60 ++++++++++++++++++++++++++++++++
 gcc/analyzer/known-functions.def | 11 ++++++
 gcc/analyzer/known-functions.h   | 14 ++++++++
 gcc/analyzer/sm-file.cc          | 74 +++++++++++++++++++++++-----------------
 5 files changed, 128 insertions(+), 32 deletions(-)
 create mode 100644 gcc/analyzer/known-functions.cc
 create mode 100644 gcc/analyzer/known-functions.def
 create mode 100644 gcc/analyzer/known-functions.h

diff --git a/gcc/analyzer/Make-plugin.in b/gcc/analyzer/Make-plugin.in
index 4501d49..10890ec 100644
--- a/gcc/analyzer/Make-plugin.in
+++ b/gcc/analyzer/Make-plugin.in
@@ -68,6 +68,7 @@ analyzer_OBJS = \
 	analyzer/digraph.o \
 	analyzer/graphviz.o \
 	analyzer/engine.o \
+	analyzer/known-functions.o \
 	analyzer/pending-diagnostic.o \
 	analyzer/program-point.o \
 	analyzer/program-state.o \
diff --git a/gcc/analyzer/known-functions.cc b/gcc/analyzer/known-functions.cc
new file mode 100644
index 0000000..2de6794
--- /dev/null
+++ b/gcc/analyzer/known-functions.cc
@@ -0,0 +1,60 @@
+/* FIXME.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "analyzer/known-functions.h"
+#include "selftest.h"
+
+/* FIXME.  */
+
+static const char *names[NUM_KNOWN_FUNCTIONS]
+{
+#define FN(ID, NAME, MIN_ARGS) NAME,
+#include "analyzer/known-functions.def"
+#undef FN
+};
+
+const gcall *
+is_call_to_known_function_p (const gimple *stmt,
+			     enum known_function *out_kf)
+{
+  const gcall *call = dyn_cast <const gcall *> (stmt);
+  if (!call)
+    return NULL;
+
+#if 0
+  int min = 0;
+  int max = NUM_KNOWN_FUNCTIONS;
+  int idx = min + (max - min) / 2 ;
+  while (min <= idx && idx <= max)
+    {
+      
+    }
+  /* FIXME: look up.  */
+  /* FIXME: check num args.  */
+#endif
+
+  return NULL;
+}
+
+// TODO: selftest
diff --git a/gcc/analyzer/known-functions.def b/gcc/analyzer/known-functions.def
new file mode 100644
index 0000000..2d5dc7b
--- /dev/null
+++ b/gcc/analyzer/known-functions.def
@@ -0,0 +1,11 @@
+/* min args, so that we can use gimple_call_arg without need to check,
+   rejecting weird non-standard signatures that would otherwise ICE.  */
+
+/* Keep alphabetized to allow for optimized lookup.  */
+
+FN(KF_fclose, "fclose", 1)
+FN(KF_fopen, "fopen", 2)
+FN(KF_free, "free", 1)
+FN(KF_malloc, "malloc", 1)
+
+/* annoyingly: just using 'malloc' leads to 'attempt to use poisoned "malloc"' */
diff --git a/gcc/analyzer/known-functions.h b/gcc/analyzer/known-functions.h
new file mode 100644
index 0000000..f20e43d
--- /dev/null
+++ b/gcc/analyzer/known-functions.h
@@ -0,0 +1,14 @@
+
+/* FIXME.  */
+
+enum known_function
+{
+  UNKNOWN_FUNCTION = -1,
+#define FN(ID, NAME, MIN_ARGS) ID,
+#include "analyzer/known-functions.def"
+#undef FN
+  NUM_KNOWN_FUNCTIONS
+};
+
+const gcall *is_call_to_known_function_p (const gimple *stmt,
+					  enum known_function *out_kf);
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index f2e8030..e10b88b 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-path.h"
 #include "diagnostic-metadata.h"
 #include "analyzer/analyzer.h"
+#include "analyzer/known-functions.h"
 #include "analyzer/pending-diagnostic.h"
 #include "analyzer/sm.h"
 
@@ -216,40 +217,49 @@ fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 				const supernode *node,
 				const gimple *stmt) const
 {
-  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+  enum known_function kf;
+  if (const gcall *call = is_call_to_known_function_p (stmt, &kf))
     {
-      if (is_named_call_p (call, "fopen", 2))
+      switch (kf)
 	{
-	  tree lhs = gimple_call_lhs (call);
-	  if (lhs)
-	    {
-	      lhs = sm_ctxt->get_readable_tree (lhs);
-	      sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked);
-	    }
-	  else
-	    {
-	      /* TODO: report leak.  */
-	    }
-	  return true;
-	}
-
-      if (is_named_call_p (call, "fclose", 1))
-	{
-	  tree arg = gimple_call_arg (call, 0);
-	  arg = sm_ctxt->get_readable_tree (arg);
-
-	  sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
-
-	  // TODO: is it safe to call fclose (NULL) ?
-	  sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_closed);
-	  sm_ctxt->on_transition (node, stmt, arg, m_null, m_closed);
-
-	  sm_ctxt->on_transition (node, stmt , arg, m_nonnull, m_closed);
-
-	  sm_ctxt->warn_for_state (node, stmt, arg, m_closed,
-				   new double_fclose (*this, arg));
-	  sm_ctxt->on_transition (node, stmt, arg, m_closed, m_stop);
-	  return true;
+	default:
+	  return false;
+
+	case KF_fopen:
+	  {
+	    tree lhs = gimple_call_lhs (call);
+	    if (lhs)
+	      {
+		lhs = sm_ctxt->get_readable_tree (lhs);
+		sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked);
+	      }
+	    else
+	      {
+		/* TODO: report leak.  */
+	      }
+	    return true;
+	  }
+	  break;
+
+	case KF_fclose:
+	  {
+	    tree arg = gimple_call_arg (call, 0);
+	    arg = sm_ctxt->get_readable_tree (arg);
+
+	    sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
+
+	    // TODO: is it safe to call fclose (NULL) ?
+	    sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_closed);
+	    sm_ctxt->on_transition (node, stmt, arg, m_null, m_closed);
+
+	    sm_ctxt->on_transition (node, stmt , arg, m_nonnull, m_closed);
+
+	    sm_ctxt->warn_for_state (node, stmt, arg, m_closed,
+				     new double_fclose (*this, arg));
+	    sm_ctxt->on_transition (node, stmt, arg, m_closed, m_stop);
+	    return true;
+	  }
+	  break;
 	}
 
       // TODO: operations on closed file
-- 
1.8.5.3

