Skip to content

gtk.cfg: g_idle_add and g_timeout_add take the data pointer - #9008

Open
pduchi wants to merge 1 commit into
cppcheck-opensource:mainfrom
pduchi:gtk-cfg-glib-source-data-ownership
Open

pduchi wants to merge 1 commit into
cppcheck-opensource:mainfrom
pduchi:gtk-cfg-glib-source-data-ownership

Conversation

@pduchi

@pduchi pduchi commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

I don't have a Trac account, so there is no ticket for this; the details are below. Feel free to create one and I'll add it to the title.

#include <glib.h>

static gboolean free_data(gpointer data)
{
    g_free(data);
    return FALSE;
}

void f(void)
{
    gchar *data = g_strdup("data");
    g_timeout_add(10, free_data, data);
}

$ cppcheck --library=gtk idle.c
idle.c:13:1: error: Memory leak: data [memleak]

GLib passes the data to the callback, and to the destroy notify of the _full variants, which commonly frees it, so there is no leak. gtk.cfg marked these functions <leak-ignore/>:

  • g_idle_add, g_idle_add_full
  • g_timeout_add, g_timeout_add_seconds, g_timeout_add_full, g_timeout_add_seconds_full

They are now <use> in the g_free memory group, without <leak-ignore/>. Their only pointer arguments are the callback, the data and the destroy notify, so <use> can't hide another leak.

This is the gtk.cfg change that #8953 (#15137) was made for: without that fix, --check-library still reported checkLibraryUseIgnore for these functions. Here <use> means what the reference describes: the function takes over the pointer.

Tests: test/cfg/gtk.c covers all six functions; the memleak false positives show without the change. cfg/gtk.cfg validates against cppcheck-cfg.rng.

The data given to g_idle_add(), g_timeout_add() and their _seconds/_full
variants is passed to the callback (and the destroy notify), which
usually frees it. With <leak-ignore/> every such call was reported as
  Memory leak: data
Configure them as <use> instead.

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

This PR causes a new false negative: leaks of a GMainContext passed to g_main_context_invoke / g_main_context_invoke_full are no longer reported. CheckLeakAutoVarImpl::functionCall applies a <use> function to every plain pointer variable in the call, because <use> has no argument number. So g_main_context_invoke(ctx, cb, NULL) now marks the context in ctx as taken over. That context comes from g_main_context_new(), which is in the g_main_context_unref group. GLib does not take ownership of the context; it only refs it internally. The base config reports Memory leak: ctx for this code; the PR does not. g_idle_add* and g_timeout_add* don't have this problem, because their only pointer parameters are the callback, the data and the destroy notify.

The memleak false positives that the PR is about are gone, and test/cfg/gtk.c passes. The new <arg> configuration for g_main_context_invoke* works: it reports a NULL callback as nullPointer and an uninitialized callback or priority as uninitvar.

Testing

Build: make -j8 cppcheck testrunner in pr/ succeeded.

cfg test: I ran test/cfg/gtk.c with the runtests.sh options (--check-library --platform=unix64 --enable=style,information --inconclusive --force --check-level=exhaustive --inline-suppr ... --library=gtk).

  • With the PR's gtk.cfg: clean, exit code 0.
  • With the base gtk.cfg: 8 memleak errors in the new test functions. So the test does cover the change.

Regression found (I compared the PR's cfg with a copy of base's gtk.cfg, both run with the PR binary, since only the cfg changed):

static gboolean cb(gpointer data) { g_free(data); return FALSE; }
void ctx_leak(void)
{
    GMainContext *ctx = g_main_context_new();
    g_main_context_invoke(ctx, cb, NULL);
}
void ctx_ok(void)
{
    GMainContext *ctx = g_main_context_new();
    g_main_context_invoke(ctx, cb, NULL);
    g_main_context_unref(ctx);
}
$ cppcheck -q --library=/tmp/b/gtkbase.cfg t2.c
t2.c:16:1: error: Memory leak: ctx [memleak]
$ cppcheck -q --library=pr/cfg/gtk.cfg t2.c
(no output)

g_main_context_invoke_full(ctx, 0, cb, NULL, NULL) behaves the same way: base reports the leak, the PR does not.

Other checks:

  • g_main_context_invoke(NULL, NULL, NULL) now gives nullPointer, and an uninitialized callback or priority gives uninitvar.
  • A malloc'd or g_new'd pointer passed as the data is no longer reported, and no mismatch is reported either.
  • A conditional if (x) g_idle_add(cb, p); is no longer reported. That matches how if (x) g_free(p); is already handled, so I don't count it as a problem of this PR.

Automated review of 51b97a0 by Claude. The pull request was built and tested locally.

Comment thread cfg/gtk.cfg Outdated
<use>g_register_data</use>
<use>g_idle_add</use>
<use>g_idle_add_full</use>
<use>g_main_context_invoke</use>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

<use> has no argument number. CheckLeakAutoVarImpl::functionCall treats every plain pointer variable in a non-leak-ignore call as taken over, and that includes the GMainContext *context argument. As a result, this leak is no longer found (base reports Memory leak: ctx, the PR reports nothing):

void f(void) {
    GMainContext *ctx = g_main_context_new();
    g_main_context_invoke(ctx, cb, NULL);
}

The same happens with g_main_context_invoke_full. GLib does not take ownership of the context. Consider adding a test/cfg/gtk.c case with // cppcheck-suppress memleak for this, and fixing it in the library or check, for example with a per-argument <use>/ownership setting. Otherwise, consider leaving the g_main_context_invoke* functions out of this change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, you're right: applies to every pointer argument, so g_main_context_invoke(ctx, ...) hid a leak of ctx. I dropped g_main_context_invoke and g_main_context_invoke_full from this PR; they keep for now.

@pduchi
pduchi force-pushed the gtk-cfg-glib-source-data-ownership branch from 51b97a0 to 46f1427 Compare November 10, 2026 10:06
@pduchi pduchi changed the title gtk.cfg: g_idle_add, g_timeout_add and g_main_context_invoke take the data pointer gtk.cfg: g_idle_add and g_timeout_add take the data pointer Nov 10, 2026

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

The earlier problem is fixed: the PR no longer touches g_main_context_invoke*, so a leaked GMainContext is reported again. The memleak false positives that the PR is about are gone, and test/cfg/gtk.c passes.

New finding (minor false negative): removing <leak-ignore/> also removes the use-after-free check for these calls. With base, g_free(p); g_idle_add(cb, p); gives deallocuse. With the PR it gives nothing. In CheckLeakAutoVarImpl::functionCall, only leak-ignore functions go through checkTokenInsideExpression; a <use> function only changes the pointer's alloc status. Every <use> function behaves this way, so this is a trade-off of the approach rather than a bug in the cfg. It could still be pinned down with a test in test/cfg/gtk.c. I found no other problems.

Earlier comments

# Comment By Where Status
1 on g_main_context_invoke* hides leaks of the GMainContext argument AI review cfg/gtk.cfg:722 ❔ Code removed

Testing

Build: make -j8 cppcheck in pr/ succeeded. Only the cfg changed, so I compared the PR's cfg/gtk.cfg with a copy of base's gtk.cfg, both run with the PR binary.

cfg test: I ran test/cfg/gtk.c with the runtests.sh options (--check-library --inline-suppr --error-exitcode=1 ... --library=gtk). It was clean, exit code 0.

Scratch test (scratch/t.c):

#include <glib.h>
#include <stdlib.h>
static gboolean cb(gpointer d){g_free(d);return FALSE;}
void a(void){ gchar *p=g_strdup("x"); g_idle_add(cb,p); g_free(p); }
void b(void){ gchar *p=g_strdup("x"); g_free(p); g_idle_add(cb,p); }
void c(void){ char *p=malloc(3); g_idle_add(cb,p); free(p); }
void d(void){ gchar *p=g_strdup("x"); gchar *q=g_strdup("y"); g_timeout_add(1,cb,p); }
void e(int x){ gchar *p=g_strdup("x"); if(x) return; g_idle_add(cb,p); }
void f(void){ g_idle_add(cb,g_strdup("x")); }
void g(void){ GMainContext *ctx=g_main_context_new(); g_main_context_invoke(ctx,cb,NULL);}
void h(void){ gchar *p=g_strdup("x"); g_idle_add_full(0,cb,p,g_free); g_free(p);}
$ cppcheck -q --library=gtkbase.cfg --template='{line}:{id}:{message}' t.c
5:deallocuse:Dereferencing 'p' after it is deallocated / released
7:memleak:Memory leak: p
7:memleak:Memory leak: q
8:memleak:Memory leak: p
10:memleak:Memory leak: ctx
9:leakNoVarFunctionCall:Allocation with g_strdup, g_idle_add doesn't release it.
$ cppcheck -q --library=pr/cfg/gtk.cfg --template='{line}:{id}:{message}' t.c
7:memleak:Memory leak: q
8:memleak:Memory leak: p
10:memleak:Memory leak: ctx
  • Lines 7, 8 and 9: the PR removes the intended false positives. The real leaks (q, and the early-return path) are still reported.
  • Line 10: the GMainContext leak from the earlier review is reported again.
  • Line 5: the deallocuse warning (use after free) is lost, which is the new false negative.
  • Lines 4 and 11: the double free through the callback is not reported, with base or with the PR.

Automated review of 46f1427 by Claude. The pull request was built and tested locally.

Comment thread cfg/gtk.cfg
<alloc init="true" no-fail="true">g_variant_type_dup_string</alloc>
<alloc init="true" no-fail="true">g_value_dup_string</alloc>
<use>g_register_data</use>
<use>g_idle_add</use>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

Taking these functions off <leak-ignore/> also turns off the use-after-free check for their arguments. CheckLeakAutoVarImpl::functionCall calls checkTokenInsideExpression only for leak-ignore functions; a <use> function only gets changeAllocStatus. As a result:

void b(void){ gchar *p=g_strdup("x"); g_free(p); g_idle_add(cb,p); }

Base reports deallocuse: Dereferencing 'p' after it is deallocated / released. With this PR nothing is reported. This is how <use> works in general, so it may be acceptable, but it is a new false negative for these six functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants