@@ -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'" } */
+}