From 6b9054381d8e77fb262f9de01a69c372ec72c3fd Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 11 Mar 2019 16:15:42 -0400
Subject: [PATCH 089/217] FIXME: initial impl of sensitive info leakage
 detector

---
 gcc/analyzer/Make-lang.in                         |   1 +
 gcc/analyzer/analyzer.cc                          |  18 ++-
 gcc/analyzer/analyzer.h                           |   1 +
 gcc/analyzer/engine.cc                            |   6 +
 gcc/analyzer/hardcoded-state-machine-sensitive.cc | 135 ++++++++++++++++++++++
 gcc/analyzer/hardcoded-state-machine.cc           |   1 +
 gcc/analyzer/hardcoded-state-machine.h            |  35 ++++++
 gcc/testsuite/gcc.dg/analyzer/sensitive-1.c       |  33 ++++++
 8 files changed, 227 insertions(+), 3 deletions(-)
 create mode 100644 gcc/analyzer/hardcoded-state-machine-sensitive.cc
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/sensitive-1.c

diff --git a/gcc/analyzer/Make-lang.in b/gcc/analyzer/Make-lang.in
index 392edf6..db632ac 100644
--- a/gcc/analyzer/Make-lang.in
+++ b/gcc/analyzer/Make-lang.in
@@ -63,6 +63,7 @@ ANALYZER_PLUGIN_SO_OBJS = \
 	analyzer/hardcoded-state-machine.o \
 	analyzer/hardcoded-state-machine-malloc.o \
 	analyzer/hardcoded-state-machine-file.o \
+	analyzer/hardcoded-state-machine-sensitive.o \
 	analyzer/hardcoded-state-machine-taint.o \
 	analyzer/supergraph.o \
 	analyzer/tristate.o \
diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index 228cac7..0e30a70 100644
--- a/gcc/analyzer/analyzer.cc
+++ b/gcc/analyzer/analyzer.cc
@@ -47,6 +47,19 @@ ana::analyzer::analyzer ()
 }
 #endif
 
+/* Helper function for checkers.  Is the CALL to the given function name?  */
+
+bool
+is_named_call_p (const gcall *call, const char *funcname)
+{
+  gcc_assert (funcname);
+
+  tree fndecl = gimple_call_fndecl (call);
+  if (!fndecl)
+    return false;
+
+  return 0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname);
+}
 
 /* Helper function for checkers.  Is the CALL to the given function name,
    and with the given number of arguments?  */
@@ -57,14 +70,13 @@ is_named_call_p (const gcall *call, const char *funcname,
 {
   gcc_assert (funcname);
 
-  tree fndecl = gimple_call_fndecl (call);
-  if (!fndecl)
+  if (!is_named_call_p (call, funcname))
     return false;
 
   if (gimple_call_num_args (call) != num_args)
     return false;
 
-  return 0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname);
+  return true;
 }
 
 // FIXME: horrible hack, so that we can use %qE, which needs subclassing of
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index d07c25b..19651a8 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 
 class graphviz_out;
 
+extern bool is_named_call_p (const gcall *call, const char *funcname);
 extern bool is_named_call_p (const gcall *call, const char *funcname,
 			     unsigned int num_args);
 
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index a6eb0a4..f2c84d5 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -2890,6 +2890,12 @@ run_checkers ()
       run_one_checker (sg, sm, the_logger.get_logger ());
     }
 
+    {
+      auto_client_timevar tv ("sensitive_state_machine");
+      sensitive_state_machine sm (the_logger.get_logger ());
+      run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
     /* end of lifetime of the_logger (so that dump file is closed after the
        various dtors run).  */
   }
diff --git a/gcc/analyzer/hardcoded-state-machine-sensitive.cc b/gcc/analyzer/hardcoded-state-machine-sensitive.cc
new file mode 100644
index 0000000..59e5cb7
--- /dev/null
+++ b/gcc/analyzer/hardcoded-state-machine-sensitive.cc
@@ -0,0 +1,135 @@
+/* FIXME.  */
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "toplev.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "context.h"
+#include "diagnostic.h"
+#include "bitmap.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-pretty-print.h"
+#include "tree-pretty-print.h"
+#include "analyzer.h"
+#include "graphviz.h"
+#include "cgraph.h"
+#include "diagnostic-path.h"
+#include "gcc-rich-location.h"
+#include <string>
+#include "supergraph.h"
+#include "hardcoded-state-machine.h"
+
+static const bool DEBUG = false;
+//static const bool DEBUG = true;
+
+////////////////////////////////////////////////////////////////////////////
+
+class leakage_through_output_file : public pending_diagnostic
+{
+public:
+  leakage_through_output_file (tree arg) : m_arg (arg) {}
+
+  void emit (rich_location *rich_loc) FINAL OVERRIDE
+  {
+    metadata m;
+    /* CWE-532: Information Exposure Through Log Files */
+    m.add_cwe (532);
+    warning_at (rich_loc, m,
+		"sensitive value %qE written to output file",
+		m_arg);
+  }
+
+private:
+  tree m_arg;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+sensitive_state_machine::sensitive_state_machine (logger *logger)
+: state_machine ("sensitive", logger)
+{
+  // TODO: parse all of this from a file:
+  m_start = add_state ("start");
+  m_sensitive = add_state ("sensitive");
+  m_stop = add_state ("stop");
+}
+
+void
+sensitive_state_machine::warn_for_any_leakage (sm_context *sm_ctxt,
+					       const supernode *node,
+					       const gimple *stmt,
+					       tree arg) const
+{
+  sm_ctxt->warn_for_state (node, stmt, arg, m_sensitive,
+			   new leakage_through_output_file (arg));
+}
+
+void
+sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
+				  const supernode *node,
+				  const gimple *stmt) const
+{
+  //tree fndecl = node->m_fun->decl;
+
+  // FIXME: do all of this from data
+  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+    {
+      if (is_named_call_p (call, "getpass", 1))
+	{
+	  tree lhs = gimple_call_lhs (call);
+	  if (lhs)
+	    sm_ctxt->on_transition (node, stmt, lhs, m_start, m_sensitive);
+	}
+      else if (is_named_call_p (call, "fprintf")
+	       || is_named_call_p (call, "printf"))
+	{
+	  /* Handle a match at any position in varargs.  */
+	  for (int idx = 1; idx < gimple_call_num_args (call); idx++)
+	    {
+	      tree arg = gimple_call_arg (call, idx);
+	      warn_for_any_leakage (sm_ctxt, node, stmt, arg);
+	    }
+	}
+      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);
+	}
+    }
+}
+
+/* FIXME.  */
+
+void
+sensitive_state_machine::on_edge (sm_context *sm_ctxt,
+				  const gimple *last_stmt,
+				  const superedge *edge) const
+{
+  /* Empty.  */
+}
+
+void
+sensitive_state_machine::on_leak (sm_context *sm_ctxt,
+				  const supernode *node,
+				  const gimple *stmt,
+				  tree var,
+				  state_machine::state_t state) const
+{
+  /* Empty.  */
+}
diff --git a/gcc/analyzer/hardcoded-state-machine.cc b/gcc/analyzer/hardcoded-state-machine.cc
index 578998c..69cedbe 100644
--- a/gcc/analyzer/hardcoded-state-machine.cc
+++ b/gcc/analyzer/hardcoded-state-machine.cc
@@ -116,4 +116,5 @@ make_checkers (auto_delete_vec <state_machine> &out, logger *logger)
   out.safe_push (new malloc_state_machine (logger));
   out.safe_push (new fileptr_state_machine (logger));
   out.safe_push (new taint_state_machine (logger));
+  out.safe_push (new sensitive_state_machine (logger));
 }
diff --git a/gcc/analyzer/hardcoded-state-machine.h b/gcc/analyzer/hardcoded-state-machine.h
index cbc30de..f92909e 100644
--- a/gcc/analyzer/hardcoded-state-machine.h
+++ b/gcc/analyzer/hardcoded-state-machine.h
@@ -218,3 +218,38 @@ private:
   state_t m_has_ub;
   state_t m_stop;
 };
+
+////////////////////////////////////////////////////////////////////////////
+
+/* A particular state machine: sensitive data, for
+   prototyping purposes.  */
+
+class sensitive_state_machine : public state_machine
+{
+public:
+  sensitive_state_machine (logger *logger);
+
+  void on_stmt (sm_context *sm_ctxt,
+		const supernode *node,
+		const gimple *stmt) const FINAL OVERRIDE;
+
+  void on_edge (sm_context *sm_ctxt,
+		const gimple *last_stmt,
+		const superedge *edge) const FINAL OVERRIDE;
+
+  void on_leak (sm_context *sm_ctxt,
+		const supernode *node,
+		const gimple *stmt,
+		tree var,
+		state_machine::state_t state) const FINAL OVERRIDE;
+
+private:
+  void warn_for_any_leakage (sm_context *sm_ctxt,
+			     const supernode *node,
+			     const gimple *stmt,
+			     tree arg) const;
+
+  state_t m_start;
+  state_t m_sensitive;
+  state_t m_stop;
+};
diff --git a/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c b/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c
new file mode 100644
index 0000000..b884179
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+char test_1 (FILE *logfile)
+{
+  char *password = getpass (">"); /* { dg-message "\\(1\\) state of 'password': 'start' -> 'sensitive'" } */
+  fprintf (logfile, "got password %s\n", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+  /* { dg-message "\\(2\\) here \\('password' is in state 'sensitive'\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_2 (FILE *logfile, int i)
+{
+  char *password = getpass (">"); /* { dg-message "\\(1\\) state of 'password': 'start' -> 'sensitive'" } */
+  fprintf (logfile, "got password[%i]: %s\n", i, password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+  /* { dg-message "\\(2\\) here \\('password' is in state 'sensitive'\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_3 (FILE *logfile)
+{
+  char *password = getpass (">"); /* { dg-message "\\(1\\) state of 'password': 'start' -> 'sensitive'" } */
+  printf ("got password %s\n", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+  /* { dg-message "\\(2\\) here \\('password' is in state 'sensitive'\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_4 (FILE *logfile)
+{
+  char *password = getpass (">"); /* { dg-message "\\(1\\) state of 'password': 'start' -> 'sensitive'" } */
+  fwrite (password, strlen (password), 1, logfile); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+  /* { dg-message "\\(2\\) here \\('password' is in state 'sensitive'\\)" "" { target *-*-* } .-1 } */
+}
+
+/* TODO: strdup etc, strcpy, memcpy, etc.  */
-- 
1.8.5.3

