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. --- gcc/analyzer/analyzer.cc | 8 ++--- gcc/analyzer/analyzer.h | 8 ++--- gcc/analyzer/sm-malloc.cc | 41 +++++++++++++++++++----- gcc/testsuite/gcc.dg/analyzer/strdup-1.c | 19 +++++++++++ 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc index ddace9a0c32..b845b86cfe1 100644 --- a/gcc/analyzer/analyzer.cc +++ b/gcc/analyzer/analyzer.cc @@ -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); diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 90143d9aba2..8de5d60821f 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -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); diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 1d69d57df0e..4f07d1f9257 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -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; diff --git a/gcc/testsuite/gcc.dg/analyzer/strdup-1.c b/gcc/testsuite/gcc.dg/analyzer/strdup-1.c index 6b950ca23a9..9ac3921af21 100644 --- a/gcc/testsuite/gcc.dg/analyzer/strdup-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/strdup-1.c @@ -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'" } */ +} -- 2.31.1