SEGFAULT encountered in wl_event_queue_release

Louis DeLosSantos louis.delos at gmail.com
Thu Dec 7 19:02:38 UTC 2023


Hello,

I'm writing a wayland application which utilizes gtk4-layer-shell and gtk4.

I'm testing how the application handles removal of monitors.
The application creates a panel on all monitors and will remove the
panel, which is a gtk4-layer-shell window, when a monitor's invalidate
signal is encountered.

However, I'm having a bit of an issue where destroying the TopLevel
gtk4-layer-shell window throws a segfault in `wl_event_queue_release`.

Oddly enough, the segfault occurs due to what seems like a corrupt
pointer to the name field in proxy->object.interface->name.

I'm using current HEAD:82d8b21827c4f58013a4061fb3c813d9ae80407e

Here is the GDB backtrace output for the invalid memory:

0x00007f40de1c4c99 in wl_log (fmt=0x38161602d000fe0 <error: Cannot
access memory at address 0x38161602d000fe0>) at
../src/wayland-util.c:449

I know there are a few components here.
For one, the client is being used by Gtk4, not myself, so it's
possible this is not an issue in Wayland but an issue of its usage in
Gtk4. However, I'm just trying to pinpoint if this is a bug and where
it exists.

I'll end with a full backtrace of the segfault followed by the source
file which does the bulk of the work:

GDB BT-----------------------------------------------------------------------------------------
#0  __strlen_evex () at ../sysdeps/x86_64/multiarch/strlen-evex.S:228
#1  0x00007f40de9354f8 in __printf_buffer
(buf=buf at entry=0x7ffe99279ae0, format=0x7f40de1c5770 "  %s#%u still
attached\n", ap=0x7ffe99279d40,
    mode_flags=mode_flags at entry=2) at
/usr/src/debug/glibc-2.38-11.fc39.x86_64/stdio-common/vfprintf-process-arg.c:435
#2  0x00007f40de956af0 in __vasprintf_internal
(result_ptr=result_ptr at entry=0x7ffe99279c40, format=<optimized out>,
args=<optimized out>, mode_flags=mode_flags at entry=2)
    at vasprintf.c:102
#3  0x00007f40de9f932a in __vasprintf_chk
(result_ptr=result_ptr at entry=0x7ffe99279c40, flag=flag at entry=2,
format=<optimized out>, ap=<optimized out>) at vasprintf_chk.c:36
#4  0x00007f40debd05d3 in vasprintf (__ap=<optimized out>,
__fmt=<optimized out>, __ptr=0x7ffe99279c40) at
/usr/include/bits/stdio2.h:169
#5  g_vasprintf (string=0x7ffe99279c40, format=<optimized out>,
args=<optimized out>) at ../glib/gprintf.c:340
#6  0x00007f40deba25d5 in g_strdup_vprintf (format=<optimized out>,
args=<optimized out>) at ../glib/gstrfuncs.c:553
#7  0x00007f40deb82f80 in format_string
(out_allocated_string=<synthetic pointer>, args=0x7ffe99279d40,
format=0x7f40de1c5770 "  %s#%u still attached\n")
    at ../glib/gmessages.c:3363
#8  g_logv (log_domain=0x7f40df4c3349 "\300t\bH\213\005\03515",
log_level=G_LOG_LEVEL_DEBUG, format=0x7f40de1c5770 "  %s#%u still
attached\n", args=0x7ffe99279d40)
    at ../glib/gmessages.c:1319
#9  0x00007f40de1c4c99 in wl_log (fmt=0x38161602d000fe0 <error: Cannot
access memory at address 0x38161602d000fe0>) at
../src/wayland-util.c:449
#10 0x00007f40de1bf595 in wl_event_queue_release
(queue=queue at entry=0x3787780) at ../src/wayland-client.c:315
#11 0x00007f40de1bf4d1 in wl_event_queue_destroy (queue=0x3787780) at
../src/wayland-client.c:352
#12 0x00007f40df3f7cf4 in gdk_wayland_surface_dispose
(object=0x3786a50) at ../gdk/wayland/gdksurface-wayland.c:538
#13 gdk_wayland_surface_dispose (object=0x3786a50) at
../gdk/wayland/gdksurface-wayland.c:522
#14 0x00007f40deae49b4 in g_object_unref (_object=0x3786a50) at
../gobject/gobject.c:3894
#15 g_object_unref (_object=0x3786a50) at ../gobject/gobject.c:3805
#16 0x00007f40df430e73 in gdk_draw_context_dispose (gobject=0x3787e70)
at ../gdk/gdkdrawcontext.c:80
#17 0x00007f40deae49b4 in g_object_unref (_object=0x3787e70) at
../gobject/gobject.c:3894
#18 g_object_unref (_object=0x3787e70) at ../gobject/gobject.c:3805
#19 0x00007f40df443708 in gdk_gl_context_make_current
(context=0x383d210) at ../gdk/gdkglcontext.c:1639
#20 gdk_gl_context_make_current (context=0x383d210) at
../gdk/gdkglcontext.c:1604
#21 0x00007f40df4781b9 in gsk_gl_renderer_unrealize
(renderer=0x383ca80) at ../gsk/gl/gskglrenderer.c:175
#22 0x00007f40df458890 in gsk_renderer_unrealize (renderer=0x383ca80)
at ../gsk/gskrenderer.c:325
#23 0x00007f40df288ada in gtk_window_unrealize (widget=0x37952c0) at
../gtk/gtkwindow.c:4478
#24 0x00007f40deaf5e85 in _g_closure_invoke_va (param_types=0x0,
n_params=<optimized out>, args=0x7ffe9927a270, instance=0x37952c0,
return_value=0x0, closure=0xf713c0)
    at ../gobject/gclosure.c:895
#25 signal_emit_valist_unlocked (instance=instance at entry=0x37952c0,
signal_id=signal_id at entry=56, detail=detail at entry=0,
var_args=var_args at entry=0x7ffe9927a270)
    at ../gobject/gsignal.c:3516
#26 0x00007f40deaf5f91 in g_signal_emit_valist (instance=0x37952c0,
signal_id=56, detail=0, var_args=var_args at entry=0x7ffe9927a270) at
../gobject/gsignal.c:3355
#27 0x00007f40deaf6053 in g_signal_emit
(instance=instance at entry=0x37952c0, signal_id=<optimized out>,
detail=detail at entry=0) at ../gobject/gsignal.c:3675
#28 0x00007f40df26d127 in gtk_widget_unrealize (widget=0x37952c0) at
../gtk/gtkwidget.c:3476
#29 0x00007f40df28c044 in gtk_window_destroy (window=0x37952c0) at
../gtk/gtkwindow.c:6802
#30 0x00007f40df27d199 in gtk_window_close (window=0x37952c0) at
../gtk/gtkwindow.c:1356
#31 gtk_window_close (window=0x37952c0) at ../gtk/gtkwindow.c:1343
#32 0x000000000040259b in _bar_free (mon=0x3817690, bar=0x0) at src/bar/bar.c:19
#33 0x00007f40deaf5e85 in _g_closure_invoke_va (param_types=0x0,
n_params=<optimized out>, args=0x7ffe9927a640, instance=0x3817690,
return_value=0x0, closure=0x38cad30)
    at ../gobject/gclosure.c:895
#34 signal_emit_valist_unlocked (instance=instance at entry=0x3817690,
signal_id=signal_id at entry=36, detail=detail at entry=0,
var_args=var_args at entry=0x7ffe9927a640)
    at ../gobject/gsignal.c:3516
#35 0x00007f40deaf5f91 in g_signal_emit_valist (instance=0x3817690,
signal_id=36, detail=0, var_args=var_args at entry=0x7ffe9927a640) at
../gobject/gsignal.c:3355
#36 0x00007f40deaf6053 in g_signal_emit
(instance=instance at entry=0x3817690, signal_id=<optimized out>,
detail=detail at entry=0) at ../gobject/gsignal.c:3675
#37 0x00007f40df3ee110 in gdk_monitor_invalidate (monitor=0x3817690)
at ../gdk/gdkmonitor.c:623
#38 gdk_wayland_display_remove_output (id=164, self=0xe5c490) at
../gdk/wayland/gdkdisplay-wayland.c:2670
#39 gdk_registry_handle_global_remove (data=0xe5c490,
registry=<optimized out>, id=164) at
../gdk/wayland/gdkdisplay-wayland.c:548
#40 0x00007f40dde16056 in ffi_call_unix64 () at ../src/x86/unix64.S:104
#41 0x00007f40dde125ef in ffi_call_int (cif=cif at entry=0x7ffe9927aa88,
fn=<optimized out>, rvalue=<optimized out>, avalue=<optimized out>,
closure=closure at entry=0x0)
--Type <RET> for more, q to quit, c to continue without paging--
    at ../src/x86/ffi64.c:673
#42 0x00007f40dde153fe in ffi_call (cif=cif at entry=0x7ffe9927aa88,
fn=<optimized out>, rvalue=rvalue at entry=0x0,
avalue=avalue at entry=0x7ffe9927a920)
    at ../src/x86/ffi64.c:710
#43 0x00007f40de1c33c3 in wl_closure_invoke (closure=0x3785db0,
flags=1, target=0xe63380, opcode=1, data=0xe5c490) at
../src/connection.c:1031
#44 0x00007f40de1c17b2 in dispatch_event
(display=display at entry=0xe569c0, queue=queue at entry=0xe56ab0) at
../src/wayland-client.c:1631
#45 0x00007f40de1c11b7 in dispatch_queue (display=0xe569c0,
queue=<optimized out>) at ../src/wayland-client.c:1777
#46 wl_display_dispatch_queue_pending (display=0xe569c0,
queue=0xe56ab0) at ../src/wayland-client.c:2019
#47 0x00007f40de1c127c in wl_display_dispatch_pending
(display=0x38161602d000fe0) at ../src/wayland-client.c:2082
#48 0x00007f40df3e8807 in _gdk_wayland_display_queue_events
(display=0xe5c490) at ../gdk/wayland/gdkeventsource.c:218
#49 0x00007f40df42c62b in gdk_display_get_event (display=0xe5c490) at
../gdk/gdkdisplay.c:466
#50 0x00007f40df3f09a6 in gdk_event_source_dispatch (base=<optimized
out>, callback=<optimized out>, data=<optimized out>) at
../gdk/wayland/gdkeventsource.c:138
#51 0x00007f40deb7de5c in g_main_dispatch (context=0xe010d0) at
../glib/gmain.c:3476
#52 g_main_context_dispatch_unlocked (context=0xe010d0) at ../glib/gmain.c:4284
#53 0x00007f40debd8dd8 in g_main_context_iterate_unlocked.isra.0
(context=context at entry=0xe010d0, block=block at entry=1,
dispatch=dispatch at entry=1, self=<optimized out>)
    at ../glib/gmain.c:4349
#54 0x00007f40deb7bad3 in g_main_context_iteration
(context=context at entry=0xe010d0, may_block=may_block at entry=1) at
../glib/gmain.c:4414
#55 0x00007f40def13c5d in g_application_run (application=0xe1b930,
argc=<optimized out>, argv=0x7ffe9927af18) at
../gio/gapplication.c:2577
#56 0x00000000004024f2 in main (argc=1, argv=0x7ffe9927af18) at src/main.c:42
----------------------------------------------------------------------------------


Source File-------------------------------------------------------------------
#include "../include/bar/bar.h"

#include <adwaita.h>
#include <gtk4-layer-shell/gtk4-layer-shell.h>

// A mapping of GdkMonitor pointers to bar_t pointers.
// When a bar_t is allocated to a monitor the association is created.
GHashTable *bars = NULL;

// Iterates over the global list of bars.
// Use @iter variable to reference the current bar of the loop.
#define BARS_ITERATE for (GSList *iter = bars; iter != NULL; iter = iter->next)

void _bar_free(GdkMonitor *mon, bar_t *bar) {
    g_debug("bar.c:_bar_free(): received monitor invalidation event.");
    // lookup bar from monitor pointer
    bar_t *bar_ptr = g_hash_table_lookup(bars, mon);
    // close window
    gtk_window_close(bar_ptr->win);
    // free bar
    free(bar_ptr);
}

// _bar_init creates a new top level gtk-layer-shell window
// for the provided monitor.
int _bar_init(GtkApplication *app, bar_t *bar, GdkMonitor *monitor) {
    if (!bar) return -1;

    bar->win = GTK_WINDOW(adw_application_window_new(app));
    gtk_application_add_window(app, bar->win);
    gtk_layer_init_for_window(bar->win);
    gtk_layer_set_layer(bar->win, GTK_LAYER_SHELL_LAYER_TOP);
    gtk_layer_auto_exclusive_zone_enable(bar->win);
    gtk_layer_set_monitor(bar->win, monitor);

    gtk_window_set_default_size(bar->win, -1, 30);

    // anchor bar to left and right corners to it is streched across
    // monitor.
    gtk_layer_set_anchor(bar->win, 0, 1);
    gtk_layer_set_anchor(bar->win, 1, 1);
    gtk_layer_set_anchor(bar->win, 2, 1);
    gtk_layer_set_anchor(bar->win, 3, 0);

    // testing...
    GtkWidget *label = gtk_label_new("Gnomeland");
    adw_application_window_set_content(ADW_APPLICATION_WINDOW(bar->win), label);
    gtk_window_present(bar->win);

    // hash monitor pointer key to bar pointer
    g_hash_table_insert(bars, monitor, bar);

    return 1;
}

// Assigns a bar to each reported monitor.
// Can be used as a callback function for the GListModel pointer returned
// from `gdk_display_get_monitors`.
//
// `user_data` is a pointer to our GtkApplication.
void _bar_assign_to_monitors(GListModel *monitors, guint position,
                             guint removed, guint added, gpointer gtk_app) {
    uint8_t n = g_list_model_get_n_items(monitors);

    g_debug("bar.c:_bar_assign_to_monitors(): current number of monitors %d",
            n);

    for (uint8_t i = 0; i < n; i++) {
        // check if bar exists fo monitor
        // if so, continue to next monitor
        if (g_hash_table_contains(bars, g_list_model_get_item(monitors, i))) {
            g_debug(
                "bar.c:_bar_assign_to_monitors(): bar already exists for "
                "monitor");
            continue;
        }

        GdkMonitor *mon = g_list_model_get_item(monitors, i);
        if (!gdk_monitor_is_valid(mon)) {
            g_warning("received invalid monitor from Gdk");
            continue;
        }

        bar_t *bar = calloc(sizeof(bar_t), 1);
        if (!_bar_init(gtk_app, bar, mon)) {
            _bar_free(mon, bar);
            g_critical(
                "bar.c:_bar_assign_to_monitors(): failed to initialize bar");
            return;
        }

        g_signal_connect(mon, "invalidate", G_CALLBACK(_bar_free), 0);

        g_debug(
            "bar.c:_bar_assign_to_monitors(): initialized bar for monitor: %s",
            gdk_monitor_get_description(mon));
    }
}

void bar_activate(AdwApplication *app, gpointer user_data) {
    GdkSeat *seat = gdk_display_get_default_seat(gdk_display_get_default());
    GdkDisplay *display = gdk_seat_get_display(seat);
    GListModel *monitors = gdk_display_get_monitors(display);

    bars = g_hash_table_new(g_direct_hash, g_direct_equal);

    // // take a ref on monitors so we can listen to changes on it.
    // g_object_ref(monitors);

    // perform an initial assignment of bars for each monitor.
    _bar_assign_to_monitors(monitors, 0, 0, 0, app);

    // Connect the callback function to the "items-changed" signal
    // so we'll destroy and recreate bars on monitor changes.
    g_signal_connect(monitors, "items-changed",
                     G_CALLBACK(_bar_assign_to_monitors), app);
}
---------------------------------------------------------------------------------------------------


More information about the wayland-devel mailing list