[committed] New warning: -Wanalyzer-jump-through-null [PR105947]

Message ID 20220805235245.2795809-1-dmalcolm@redhat.com
State Committed
Commit e1a9168153d2bf12695844a9ca9f9fc1de8d1ddf
Headers
Series [committed] New warning: -Wanalyzer-jump-through-null [PR105947] |

Commit Message

David Malcolm Aug. 5, 2022, 11:52 p.m. UTC
  This patch adds a new warning to -fanalyzer for jumps through NULL
function pointers.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r13-1979-ge1a9168153d2bf.

gcc/analyzer/ChangeLog:
	PR analyzer/105947
	* analyzer.opt (Wanalyzer-jump-through-null): New option.
	* engine.cc (class jump_through_null): New.
	(exploded_graph::process_node): Complain about jumps through NULL
	function pointers.

gcc/ChangeLog:
	PR analyzer/105947
	* doc/invoke.texi: Add -Wanalyzer-jump-through-null.

gcc/testsuite/ChangeLog:
	PR analyzer/105947
	* gcc.dg/analyzer/function-ptr-5.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/analyzer/analyzer.opt                     |  4 ++
 gcc/analyzer/engine.cc                        | 49 +++++++++++++++++++
 gcc/doc/invoke.texi                           | 12 +++++
 .../gcc.dg/analyzer/function-ptr-5.c          | 42 ++++++++++++++++
 4 files changed, 107 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/function-ptr-5.c
  

Patch

diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 808ff36ac54..c6d9c53d9c3 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -98,6 +98,10 @@  Wanalyzer-free-of-non-heap
 Common Var(warn_analyzer_free_of_non_heap) Init(1) Warning
 Warn about code paths in which a non-heap pointer is freed.
 
+Wanalyzer-jump-through-null
+Common Var(warn_analyzer_jump_through_null) Init(1) Warning
+Warn about code paths in which a NULL function pointer is called.
+
 Wanalyzer-malloc-leak
 Common Var(warn_analyzer_malloc_leak) Init(1) Warning
 Warn about code paths in which a heap-allocated pointer leaks.
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 85b7c5e1227..e8db00d7e18 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -3705,6 +3705,46 @@  private:
   bool m_terminate_path;
 };
 
+/* A subclass of pending_diagnostic for complaining about jumps through NULL
+   function pointers.  */
+
+class jump_through_null : public pending_diagnostic_subclass<jump_through_null>
+{
+public:
+  jump_through_null (const gcall *call)
+  : m_call (call)
+  {}
+
+  const char *get_kind () const final override
+  {
+    return "jump_through_null";
+  }
+
+  bool operator== (const jump_through_null &other) const
+  {
+    return m_call == other.m_call;
+  }
+
+  int get_controlling_option () const final override
+  {
+    return OPT_Wanalyzer_jump_through_null;
+  }
+
+  bool emit (rich_location *rich_loc) final override
+  {
+    return warning_at (rich_loc, get_controlling_option (),
+		       "jump through null pointer");
+  }
+
+  label_text describe_final_event (const evdesc::final_event &ev) final override
+  {
+    return ev.formatted_print ("jump through null pointer here");
+  }
+
+private:
+  const gcall *m_call;
+};
+
 /* The core of exploded_graph::process_worklist (the main analysis loop),
    handling one node in the worklist.
 
@@ -4046,6 +4086,15 @@  exploded_graph::process_node (exploded_node *node)
 							       logger);
 		if (!call_discovered)
 		  {
+		    /* Check for jump through NULL.  */
+		    if (tree fn_ptr = gimple_call_fn (call))
+		      {
+			const svalue *fn_ptr_sval
+			  = model->get_rvalue (fn_ptr, &ctxt);
+			if (fn_ptr_sval->all_zeroes_p ())
+			  ctxt.warn (new jump_through_null (call));
+		      }
+
 		    /* An unknown function or a special function was called
 		       at this point, in such case, don't terminate the
 		       analysis of the current function.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e8cd60103e4..c6dac6a1273 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -453,6 +453,7 @@  Objective-C and Objective-C++ Dialects}.
 -Wno-analyzer-fd-use-without-check @gol
 -Wno-analyzer-file-leak @gol
 -Wno-analyzer-free-of-non-heap @gol
+-Wno-analyzer-jump-through-null @gol
 -Wno-analyzer-malloc-leak @gol
 -Wno-analyzer-mismatching-deallocation @gol
 -Wno-analyzer-null-argument @gol
@@ -9756,6 +9757,7 @@  Enabling this option effectively enables the following warnings:
 -Wanalyzer-fd-use-without-check @gol
 -Wanalyzer-file-leak @gol
 -Wanalyzer-free-of-non-heap @gol
+-Wanalyzer-jump-through-null @gol
 -Wanalyzer-malloc-leak @gol
 -Wanalyzer-mismatching-deallocation @gol
 -Wanalyzer-null-argument @gol
@@ -9942,6 +9944,16 @@  is called on a non-heap pointer (e.g. an on-stack buffer, or a global).
 
 See @uref{https://cwe.mitre.org/data/definitions/590.html, CWE-590: Free of Memory not on the Heap}.
 
+@item -Wno-analyzer-jump-through-null
+@opindex Wanalyzer-jump-through-null
+@opindex Wno-analyzer-jump-through-null
+This warning requires @option{-fanalyzer}, which enables it; use
+@option{-Wno-analyzer-jump-through-null}
+to disable it.
+
+This diagnostic warns for paths through the code in which a @code{NULL}
+function pointer is called.
+
 @item -Wno-analyzer-malloc-leak
 @opindex Wanalyzer-malloc-leak
 @opindex Wno-analyzer-malloc-leak
diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-5.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-5.c
new file mode 100644
index 00000000000..3c46f289082
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-5.c
@@ -0,0 +1,42 @@ 
+#define NULL ((void *)0)
+
+void calling_null_fn_ptr_1 (void)
+{
+  void (*fn_ptr) (void) = NULL;
+  fn_ptr (); /* { dg-warning "jump through null pointer" } */
+}
+
+int calling_null_fn_ptr_2 (void)
+{
+  int (*fn_ptr) (void) = NULL;
+  return fn_ptr (); /* { dg-warning "jump through null pointer" } */
+}
+
+typedef void (*void_void_fn_ptr) (void);
+
+void calling_const_fn_ptr (void)
+{
+  void_void_fn_ptr fn_ptr = (void_void_fn_ptr)0xffd2;
+  return fn_ptr ();
+}
+
+void skipping_init (int flag)
+{
+  void_void_fn_ptr fn_ptr = NULL;
+  if (flag) /* { dg-message "branch" } */
+    fn_ptr = (void_void_fn_ptr)0xffd2;
+  fn_ptr (); /* { dg-warning "jump through null pointer" } */
+}
+
+struct callbacks
+{
+  void_void_fn_ptr on_redraw;
+  void_void_fn_ptr on_cleanup;  
+};
+
+void test_callbacks (void)
+{
+  struct callbacks cb;
+  __builtin_memset (&cb, 0, sizeof (cb));
+  cb.on_cleanup (); /* { dg-warning "jump through null pointer" } */
+}