Common GLib Programming Errors, Part Two: Weak Pointers

This post is a sequel to Common GLib Programming Errors, where I covered four common errors: failure to disconnect a signal handler, misuse of a GSource handle ID, failure to cancel an asynchronous function, and misuse of main contexts in library or threaded code. Although there are many ways to mess up when writing programs that use GLib, I believe the first post covered the most likely and most pernicious… except I missed weak pointers. Sébastien pointed out that these should be covered too, so here we are.

Mistake #5: Failure to Disconnect Weak Pointer

In object-oriented languages, weak pointers are a safety improvement. The idea is to hold a non-owning pointer to an object that gets automatically set to NULL when that object is destroyed to prevent use-after-free vulnerabilities. However, this only works well because object-oriented languages have destructors. Without destructors, we have to deregister the weak pointer manually, and failure to do so is a disaster that will result in memory corruption that’s extremely difficult to track down. For example:

static void
a_start_watching_b (A *self,
                    B *b)
{
  // Keep a weak reference to b. When b is destroyed,
  // self->b will automatically be set to NULL.
  self->b = b;
  g_object_add_weak_pointer (b, &self->b);
}

static void
a_do_something_with_b (Foo *self)
{
  if (self->b) {
    // Do something safely here, knowing that b
    // is assuredly still alive. This avoids a
    // use-after-free vulnerability if b is destroyed,
    // i.e. self->b cannot be dangling.
  }
}

Let’s say that the Bar in this example outlives the Foo, but Foo failed to call g_object_remove_weak_pointer() . Then when Bar is destroyed later, the memory that used to be occupied by self->bar will get clobbered with NULL. Hopefully that will result in an immediate crash. If not, good luck trying to debug what’s going wrong when some innocent variable elsewhere in your program gets randomly clobbered. This is often results in a frustrating wild goose chase when trying to track down what is going wrong (example).

The solution is to always disconnect your weak pointer. In most cases, your dispose function is the best place to do this:

static void
a_dispose (GObject *object)
{
  A *a = (A *)object;
  g_clear_weak_pointer (&a->b);
  G_OBJECT_CLASS (a_parent_class)->dispose (object);
}

Note that g_clear_weak_pointer() is equivalent to:

if (a->b) {
  g_object_remove_weak_pointer (a->b, &a->b);
  a->b = NULL;
}

but you probably guessed that, because it follows the same pattern as the other clear functions that we’ve used so far.

2 Replies to “Common GLib Programming Errors, Part Two: Weak Pointers”

  1. Thanks! ;-)

    I’ve just thought about a simplistic static analysis tool to catch these errors early on:

    The number of g_object_add_weak_pointer() calls in a *.c file must match the number of g_object_remove_weak_pointer() (or equivalent functions). With a small shell script and grep.

    This can give lots of false positives, but where such a tool would show a warning, it’s worthwhile to check the *.c file.

    (And as with every static analysis tool giving false positives, if they are always run as part of a testsuite, to silence the false positives there is always the possibility to add a #define or special comment, or to have a list of files to ignore).

  2. BTW, such pitfalls to avoid with GLib, this could be useful to write on developer.gnome.org or the small book that I’ve written https://gitlab.gnome.org/swilmet/glib-gtk-book , as standalone documents or an appendix.

    With a CC-BY-SA license.

    We can also compile a list of blog posts and such, in the meantime. Also for the history of some parts of GNOME (I’m especially thinking about some ebassi’s or Federico’s blog posts).

Comments are closed.