This patch teaches the GCC static analyzer to recognize GLib's memory
allocation and deallocation functions, preventing false negatives for
memory leaks in GTK/GNOME applications.

gcc/analyzer/ChangeLog:
        * sm-malloc.cc (known_allocator_p): Add checks for g_malloc,
        g_malloc0, g_realloc, g_new, and g_new0.
        (get_deallocator): Add check for g_free.
        (malloc_state_machine::on_call): Add check for g_free.

gcc/testsuite/ChangeLog:
        * gcc.dg/analyzer/glib-malloc-leak.c: New test.

Signed-off-by: dishu305 <[email protected]>
---
 gcc/analyzer/sm-malloc.cc                     | 13 +++++++---
 .../gcc.dg/analyzer/glib-malloc-leak.c        | 26 +++++++++++++++++++
 2 files changed, 36 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/glib-malloc-leak.c

diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 72e6e28e2..ff8e19f0b 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -1964,7 +1964,8 @@ malloc_state_machine::get_or_create_deallocator (tree 
deallocator_fndecl)
   deallocator *d;
   if (is_named_call_p (deallocator_fndecl, "free")
       || is_std_named_call_p (deallocator_fndecl, "free")
-      || is_named_call_p (deallocator_fndecl, "__builtin_free"))
+      || is_named_call_p (deallocator_fndecl, "__builtin_free")
+      || is_named_call_p (deallocator_fndecl, "g_free"))
     d = &m_free.m_deallocator;
   else
     {
@@ -2006,7 +2007,12 @@ known_allocator_p (const_tree fndecl, const gcall &call)
       || 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))
+      || is_named_call_p (fndecl, "strndup", call, 2)
+      || is_named_call_p (fndecl, "g_malloc", call, 1)
+      || is_named_call_p (fndecl, "g_malloc0", call, 1)
+      || is_named_call_p (fndecl, "g_realloc", call, 2)
+      || is_named_call_p (fndecl, "g_new", call, 2)
+      || is_named_call_p (fndecl, "g_new0", call, 2))
     return true;
 
   /* ... or it is a builtin allocator that allocates objects freed with
@@ -2161,7 +2167,8 @@ 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))
+           || is_named_call_p (callee_fndecl, "__builtin_free", call, 1)
+       || is_named_call_p (callee_fndecl, "g_free", call, 1))
          {
            on_deallocator_call (sm_ctxt, call,
                                 &m_free.m_deallocator, 0);
diff --git a/gcc/testsuite/gcc.dg/analyzer/glib-malloc-leak.c 
b/gcc/testsuite/gcc.dg/analyzer/glib-malloc-leak.c
new file mode 100644
index 000000000..0a5622c1f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/glib-malloc-leak.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-options "-fanalyzer" } */
+
+/* Test that the analyzer recognizes g_malloc and g_free.
+   It should report a leak for ptr1, but not for ptr2. */
+
+extern void *g_malloc (unsigned long);
+extern void g_free (void *);
+
+void test_leak (void)
+{
+  void *ptr1 = g_malloc (100); /* { dg-warning "leak of 'ptr1'" } */
+}
+
+void test_no_leak (void)
+{
+  void *ptr2 = g_malloc (200);
+  g_free (ptr2);
+}
+
+int main (void)
+{
+  test_leak ();
+  test_no_leak ();
+  return 0;
+}
-- 
2.43.0

Reply via email to