[gcc r10-6981] analyzer: detect malloc, free, calloc within "std" [PR93959]

David Malcolm dmalcolm@gcc.gnu.org
Mon Mar 2 21:41:00 GMT 2020


https://gcc.gnu.org/g:9f00b22f98ec0688fcd9816a03aa3f7eea58bcf7

commit r10-6981-g9f00b22f98ec0688fcd9816a03aa3f7eea58bcf7
Author: David Malcolm <dmalcolm@redhat.com>
Date:   Thu Feb 27 14:19:33 2020 -0500

    analyzer: detect malloc, free, calloc within "std" [PR93959]
    
    PR analyzer/93959 reported that g++.dg/analyzer/malloc.C was failing
    with no output on Solaris.
    
    The issue is that <stdlib.h> there has "using std::free;", converting
    all the "free" calls to std::free, which fails the name-matching via
    is_named_call_p.
    
    This patch implements an is_std_named_call_p variant of is_named_call_p
    to check for the name within "std", and uses it in sm-malloc.c to check
    for std::malloc, std::calloc, and std::free.
    
    gcc/analyzer/ChangeLog:
    	PR analyzer/93959
    	* analyzer.cc (is_std_function_p): New function.
    	(is_std_named_call_p): New functions.
    	* analyzer.h (is_std_named_call_p): New decl.
    	* sm-malloc.cc (malloc_state_machine::on_stmt): Check for "std::"
    	variants when checking for malloc, calloc and free.
    
    gcc/testsuite/ChangeLog:
    	PR analyzer/93959
    	* g++.dg/analyzer/cstdlib-2.C: New test.
    	* g++.dg/analyzer/cstdlib.C: New test.

Diff:
---
 gcc/analyzer/ChangeLog                    |  9 +++++
 gcc/analyzer/analyzer.cc                  | 61 +++++++++++++++++++++++++++++++
 gcc/analyzer/analyzer.h                   |  2 +
 gcc/analyzer/sm-malloc.cc                 |  3 ++
 gcc/testsuite/ChangeLog                   |  6 +++
 gcc/testsuite/g++.dg/analyzer/cstdlib-2.C | 25 +++++++++++++
 gcc/testsuite/g++.dg/analyzer/cstdlib.C   | 17 +++++++++
 7 files changed, 123 insertions(+)

diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog
index 5fbaec3..e5d7bdb 100644
--- a/gcc/analyzer/ChangeLog
+++ b/gcc/analyzer/ChangeLog
@@ -1,3 +1,12 @@
+2020-03-02  David Malcolm  <dmalcolm@redhat.com>
+
+	PR analyzer/93959
+	* analyzer.cc (is_std_function_p): New function.
+	(is_std_named_call_p): New functions.
+	* analyzer.h (is_std_named_call_p): New decl.
+	* sm-malloc.cc (malloc_state_machine::on_stmt): Check for "std::"
+	variants when checking for malloc, calloc and free.
+
 2020-02-26  David Malcolm  <dmalcolm@redhat.com>
 
 	PR analyzer/93950
diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index 5cf745e..8bc3ce4 100644
--- a/gcc/analyzer/analyzer.cc
+++ b/gcc/analyzer/analyzer.cc
@@ -86,6 +86,49 @@ is_named_call_p (tree fndecl, const char *funcname)
   return 0 == strcmp (tname, funcname);
 }
 
+/* Return true if FNDECL is within the namespace "std".
+   Compare with cp/typeck.c: decl_in_std_namespace_p, but this doesn't
+   rely on being the C++ FE (or handle inline namespaces inside of std).  */
+
+static inline bool
+is_std_function_p (const_tree fndecl)
+{
+  tree name_decl = DECL_NAME (fndecl);
+  if (!name_decl)
+    return false;
+  if (!DECL_CONTEXT (fndecl))
+    return false;
+  if (TREE_CODE (DECL_CONTEXT (fndecl)) != NAMESPACE_DECL)
+    return false;
+  tree ns = DECL_CONTEXT (fndecl);
+  if (!(DECL_CONTEXT (ns) == NULL_TREE
+	|| TREE_CODE (DECL_CONTEXT (ns)) == TRANSLATION_UNIT_DECL))
+    return false;
+  if (!DECL_NAME (ns))
+    return false;
+  return id_equal ("std", DECL_NAME (ns));
+}
+
+/* Like is_named_call_p, but look for std::FUNCNAME.  */
+
+bool
+is_std_named_call_p (tree fndecl, const char *funcname)
+{
+  gcc_assert (fndecl);
+  gcc_assert (funcname);
+
+  if (!is_std_function_p (fndecl))
+    return false;
+
+  tree identifier = DECL_NAME (fndecl);
+  const char *name = IDENTIFIER_POINTER (identifier);
+  const char *tname = name;
+
+  /* Don't disregard prefix _ or __ in FNDECL's name.  */
+
+  return 0 == strcmp (tname, funcname);
+}
+
 /* Helper function for checkers.  Is FNDECL an extern fndecl at file scope
    that has the given FUNCNAME, and does CALL have the given number of
    arguments?  */
@@ -106,6 +149,24 @@ is_named_call_p (tree fndecl, const char *funcname,
   return true;
 }
 
+/* Like is_named_call_p, but check for std::FUNCNAME.  */
+
+bool
+is_std_named_call_p (tree fndecl, const char *funcname,
+		     const gcall *call, unsigned int num_args)
+{
+  gcc_assert (fndecl);
+  gcc_assert (funcname);
+
+  if (!is_std_named_call_p (fndecl, funcname))
+    return false;
+
+  if (gimple_call_num_args (call) != num_args)
+    return false;
+
+  return true;
+}
+
 /* Return true if stmt is a setjmp or sigsetjmp call.  */
 
 bool
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 1ae76cc..5364edb 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -78,6 +78,8 @@ extern bool is_special_named_call_p (const gcall *call, const char *funcname,
 extern bool is_named_call_p (tree fndecl, const char *funcname);
 extern bool is_named_call_p (tree fndecl, const char *funcname,
 			     const gcall *call, unsigned int num_args);
+extern bool is_std_named_call_p (tree fndecl, const char *funcname,
+				 const gcall *call, unsigned int num_args);
 extern bool is_setjmp_call_p (const gcall *call);
 extern bool is_longjmp_call_p (const gcall *call);
 
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 46225b6..aaef695 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -611,6 +611,8 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
       {
 	if (is_named_call_p (callee_fndecl, "malloc", call, 1)
 	    || is_named_call_p (callee_fndecl, "calloc", call, 2)
+	    || is_std_named_call_p (callee_fndecl, "malloc", call, 1)
+	    || is_std_named_call_p (callee_fndecl, "calloc", call, 2)
 	    || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1)
 	    || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2))
 	  {
@@ -640,6 +642,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	  }
 
 	if (is_named_call_p (callee_fndecl, "free", call, 1)
+	    || is_std_named_call_p (callee_fndecl, "free", call, 1)
 	    || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
 	  {
 	    tree arg = gimple_call_arg (call, 0);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index af29e81..33fa9bc 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2020-03-02  David Malcolm  <dmalcolm@redhat.com>
+
+	PR analyzer/93959
+	* g++.dg/analyzer/cstdlib-2.C: New test.
+	* g++.dg/analyzer/cstdlib.C: New test.
+
 2020-03-02  Iain Sandoe  <iain@sandoe.co.uk>
 	    Jun Ma <JunMa@linux.alibaba.com>
 
diff --git a/gcc/testsuite/g++.dg/analyzer/cstdlib-2.C b/gcc/testsuite/g++.dg/analyzer/cstdlib-2.C
new file mode 100644
index 0000000..0dedf8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/cstdlib-2.C
@@ -0,0 +1,25 @@
+/* Manual reimplemenation of <cstdlib>, to test name-matching within std.  */
+
+namespace std
+{
+  typedef __SIZE_TYPE__ size_t;
+  void *malloc (std::size_t size);
+  void *calloc (std::size_t num, std::size_t size);
+  void free (void *ptr);
+}
+
+void test_1 (void *ptr)
+{
+  std::free (ptr); /* { dg-message "first 'free' here" } */
+  std::free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void)
+{
+  void *p = std::malloc (1024); /* { dg-message "allocated here" } */
+} /* { dg-warning "leak of 'p'" } */
+
+void test_3 (void)
+{
+  void *p = std::calloc (42, 1024); /* { dg-message "allocated here" } */
+} /* { dg-warning "leak of 'p'" } */
diff --git a/gcc/testsuite/g++.dg/analyzer/cstdlib.C b/gcc/testsuite/g++.dg/analyzer/cstdlib.C
new file mode 100644
index 0000000..ec6327b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/cstdlib.C
@@ -0,0 +1,17 @@
+#include <cstdlib>
+
+void test_1 (void *ptr)
+{
+  std::free (ptr); /* { dg-message "first 'free' here" } */
+  std::free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void)
+{
+  void *p = std::malloc (1024); /* { dg-message "allocated here" } */
+} /* { dg-warning "leak of 'p'" } */
+
+void test_3 (void)
+{
+  void *p = std::calloc (42, 1024); /* { dg-message "allocated here" } */
+} /* { dg-warning "leak of 'p'" } */



More information about the Gcc-cvs mailing list