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>
PR analyzer/93950
......
......@@ -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
......
......@@ -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);
......
......@@ -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);
......
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>
......
/* 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