Commit 9f00b22f by David Malcolm

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.
parent cd14f288
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> 2020-02-26 David Malcolm <dmalcolm@redhat.com>
PR analyzer/93950 PR analyzer/93950
......
...@@ -86,6 +86,49 @@ is_named_call_p (tree fndecl, const char *funcname) ...@@ -86,6 +86,49 @@ is_named_call_p (tree fndecl, const char *funcname)
return 0 == strcmp (tname, 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 /* 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 that has the given FUNCNAME, and does CALL have the given number of
arguments? */ arguments? */
...@@ -106,6 +149,24 @@ is_named_call_p (tree fndecl, const char *funcname, ...@@ -106,6 +149,24 @@ is_named_call_p (tree fndecl, const char *funcname,
return true; 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. */ /* Return true if stmt is a setjmp or sigsetjmp call. */
bool bool
......
...@@ -78,6 +78,8 @@ extern bool is_special_named_call_p (const gcall *call, const char *funcname, ...@@ -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);
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); 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_setjmp_call_p (const gcall *call);
extern bool is_longjmp_call_p (const gcall *call); extern bool is_longjmp_call_p (const gcall *call);
......
...@@ -611,6 +611,8 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt, ...@@ -611,6 +611,8 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
{ {
if (is_named_call_p (callee_fndecl, "malloc", call, 1) if (is_named_call_p (callee_fndecl, "malloc", call, 1)
|| is_named_call_p (callee_fndecl, "calloc", call, 2) || 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_malloc", call, 1)
|| is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2)) || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2))
{ {
...@@ -640,6 +642,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt, ...@@ -640,6 +642,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
} }
if (is_named_call_p (callee_fndecl, "free", call, 1) 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)) || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
{ {
tree arg = gimple_call_arg (call, 0); tree arg = gimple_call_arg (call, 0);
......
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> 2020-03-02 Iain Sandoe <iain@sandoe.co.uk>
Jun Ma <JunMa@linux.alibaba.com> Jun Ma <JunMa@linux.alibaba.com>
......
/* 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'" } */
#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'" } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment