]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
analyzer: Handle strdup builtins
authorSiddhesh Poyarekar <siddhesh@gotplt.org>
Wed, 28 Jul 2021 10:13:47 +0000 (15:43 +0530)
committerSiddhesh Poyarekar <siddhesh@gotplt.org>
Wed, 28 Jul 2021 12:13:26 +0000 (17:43 +0530)
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
gcc/analyzer/analyzer.h
gcc/analyzer/sm-malloc.cc
gcc/testsuite/gcc.dg/analyzer/strdup-1.c

index ddace9a0c321849ce6c6910aa12210ebf95b5201..b845b86cfe1bb037d8e407ce03f0907a1ba730f4 100644 (file)
@@ -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);
index 90143d9aba23bd3cd962a5fa71acf147f743bd01..8de5d60821fd7c86bf68e3bd7dbc8edfb74daec9 100644 (file)
@@ -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);
index 1d69d57df0ed7d9fd829a4718db4e49337c87075..4f07d1f9257c345f4cc1c1524073c986d1fabbd6 100644 (file)
@@ -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;
index 6b950ca23a99d37d526c9ed5c651c6931537630d..9ac3921af21271f5d2ea4173c8f40adae8877fab 100644 (file)
@@ -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'" } */
+}