From 9f00b22f98ec0688fcd9816a03aa3f7eea58bcf7 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Thu, 27 Feb 2020 14:19:33 -0500 Subject: [PATCH] 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 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. --- 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(+) create mode 100644 gcc/testsuite/g++.dg/analyzer/cstdlib-2.C create mode 100644 gcc/testsuite/g++.dg/analyzer/cstdlib.C diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index 5fbaec389aa..e5d7bdbbf7e 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,12 @@ +2020-03-02 David Malcolm + + 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 PR analyzer/93950 diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc index 5cf745ea632..8bc3ce49f07 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 1ae76cc4ea0..5364edb3d96 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 46225b6f700..aaef6959362 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 af29e81a0b6..33fa9bc972b 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2020-03-02 David Malcolm + + PR analyzer/93959 + * g++.dg/analyzer/cstdlib-2.C: New test. + * g++.dg/analyzer/cstdlib.C: New test. + 2020-03-02 Iain Sandoe Jun Ma 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 00000000000..0dedf8aef5c --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/cstdlib-2.C @@ -0,0 +1,25 @@ +/* Manual reimplemenation of , 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 00000000000..ec6327bf884 --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/cstdlib.C @@ -0,0 +1,17 @@ +#include + +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'" } */ -- 2.30.2