analyzer: Handle strdup builtins
Consolidate allocator builtin handling and add support for __builtin_strdup and __builtin_strndup. gcc/analyzer/ChangeLog: * analyzer.cc (is_named_call_p, is_std_named_call_p): Make first argument a const_tree. * analyzer.h (is_named_call_p, -s_std_named_call_p): Likewise. * sm-malloc.cc (known_allocator_p): New function. (malloc_state_machine::on_stmt): Use it. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/strdup-1.c (test_4, test_5, test_6): New tests.
This commit is contained in:
parent
84606efb0c
commit
31534ac26e
@ -240,7 +240,7 @@ is_special_named_call_p (const gcall *call, const char *funcname,
|
||||
Compare with special_function_p in calls.c. */
|
||||
|
||||
bool
|
||||
is_named_call_p (tree fndecl, const char *funcname)
|
||||
is_named_call_p (const_tree fndecl, const char *funcname)
|
||||
{
|
||||
gcc_assert (fndecl);
|
||||
gcc_assert (funcname);
|
||||
@ -292,7 +292,7 @@ is_std_function_p (const_tree fndecl)
|
||||
/* Like is_named_call_p, but look for std::FUNCNAME. */
|
||||
|
||||
bool
|
||||
is_std_named_call_p (tree fndecl, const char *funcname)
|
||||
is_std_named_call_p (const_tree fndecl, const char *funcname)
|
||||
{
|
||||
gcc_assert (fndecl);
|
||||
gcc_assert (funcname);
|
||||
@ -314,7 +314,7 @@ is_std_named_call_p (tree fndecl, const char *funcname)
|
||||
arguments? */
|
||||
|
||||
bool
|
||||
is_named_call_p (tree fndecl, const char *funcname,
|
||||
is_named_call_p (const_tree fndecl, const char *funcname,
|
||||
const gcall *call, unsigned int num_args)
|
||||
{
|
||||
gcc_assert (fndecl);
|
||||
@ -332,7 +332,7 @@ is_named_call_p (tree fndecl, const char *funcname,
|
||||
/* Like is_named_call_p, but check for std::FUNCNAME. */
|
||||
|
||||
bool
|
||||
is_std_named_call_p (tree fndecl, const char *funcname,
|
||||
is_std_named_call_p (const_tree fndecl, const char *funcname,
|
||||
const gcall *call, unsigned int num_args)
|
||||
{
|
||||
gcc_assert (fndecl);
|
||||
|
@ -220,11 +220,11 @@ enum access_direction
|
||||
|
||||
extern bool is_special_named_call_p (const gcall *call, const char *funcname,
|
||||
unsigned int num_args);
|
||||
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 (const_tree fndecl, const char *funcname);
|
||||
extern bool is_named_call_p (const_tree fndecl, const char *funcname,
|
||||
const gcall *call, unsigned int num_args);
|
||||
extern bool is_std_named_call_p (tree fndecl, const char *funcname);
|
||||
extern bool is_std_named_call_p (tree fndecl, const char *funcname,
|
||||
extern bool is_std_named_call_p (const_tree fndecl, const char *funcname);
|
||||
extern bool is_std_named_call_p (const_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);
|
||||
|
@ -1526,6 +1526,38 @@ malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl)
|
||||
return d;
|
||||
}
|
||||
|
||||
/* Try to identify the function declaration either by name or as a known malloc
|
||||
builtin. */
|
||||
|
||||
static bool
|
||||
known_allocator_p (const_tree fndecl, const gcall *call)
|
||||
{
|
||||
/* Either it is a function we know by name and number of arguments... */
|
||||
if (is_named_call_p (fndecl, "malloc", call, 1)
|
||||
|| is_named_call_p (fndecl, "calloc", call, 2)
|
||||
|| is_std_named_call_p (fndecl, "malloc", call, 1)
|
||||
|| is_std_named_call_p (fndecl, "calloc", call, 2)
|
||||
|| is_named_call_p (fndecl, "strdup", call, 1)
|
||||
|| is_named_call_p (fndecl, "strndup", call, 2))
|
||||
return true;
|
||||
|
||||
/* ... or it is a builtin allocator that allocates objects freed with
|
||||
__builtin_free. */
|
||||
if (fndecl_built_in_p (fndecl))
|
||||
switch (DECL_FUNCTION_CODE (fndecl))
|
||||
{
|
||||
case BUILT_IN_MALLOC:
|
||||
case BUILT_IN_CALLOC:
|
||||
case BUILT_IN_STRDUP:
|
||||
case BUILT_IN_STRNDUP:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
|
||||
|
||||
bool
|
||||
@ -1536,14 +1568,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
|
||||
if (const gcall *call = dyn_cast <const gcall *> (stmt))
|
||||
if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
|
||||
{
|
||||
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)
|
||||
|| is_named_call_p (callee_fndecl, "strdup", call, 1)
|
||||
|| is_named_call_p (callee_fndecl, "strndup", call, 2))
|
||||
if (known_allocator_p (callee_fndecl, call))
|
||||
{
|
||||
on_allocator_call (sm_ctxt, call, &m_free);
|
||||
return true;
|
||||
|
@ -14,8 +14,27 @@ void test_2 (const char *s)
|
||||
char *p = strdup (s);
|
||||
free (p);
|
||||
}
|
||||
|
||||
void test_3 (const char *s)
|
||||
{
|
||||
char *p = strdup (s); /* { dg-message "this call could return NULL" } */
|
||||
requires_nonnull (p); /* { dg-warning "use of possibly-NULL 'p'" } */
|
||||
}
|
||||
|
||||
/* Repeat tests for __builtin_strdup. */
|
||||
void test_4 (const char *s)
|
||||
{
|
||||
char *p = __builtin_strdup (s); /* { dg-message "allocated here" } */
|
||||
} /* { dg-warning "leak of 'p'" } */
|
||||
|
||||
void test_5 (const char *s)
|
||||
{
|
||||
char *p = __builtin_strdup (s);
|
||||
free (p);
|
||||
}
|
||||
|
||||
void test_6 (const char *s)
|
||||
{
|
||||
char *p = __builtin_strdup (s); /* { dg-message "this call could return NULL" } */
|
||||
requires_nonnull (p); /* { dg-warning "use of possibly-NULL 'p'" } */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user