GTK crash when changing style from `size-allocate` signal
Problem occurs because gtk_css_gadget_allocate()
has code:
style = gtk_css_gadget_get_style (gadget);
...
GTK_CSS_GADGET_GET_CLASS (gadget)->allocate (gadget, &content_allocation, baseline, &content_clip);
...
_gtk_css_shadows_value_get_extents (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BOX_SHADOW), &shadow);
there, style
is cached before allocate()
and then used after, but it could change and be freed in allocate()
.
Steps to reproduce
Use snippet below. Snippet is minimized from real application. In it, pending colorings happen to apply from size-allocate
signal handler.
#include <gtk/gtk.h>
GtkWidget* g_window = 0;
GtkWidget* g_scrolled = 0;
GtkWidget* g_box = 0;
GtkWidget* g_tree = 0;
int g_numInvalidates = 0;
void on_size_allocate(GtkWidget* self, GtkAllocation* allocation, gpointer user_data)
{
g_numInvalidates++;
printf("on_size_allocate %p\n", self);
GtkWidget* widgets[] = {
// g_window,
g_box,
// g_scrolled,
g_tree,
};
for (GtkWidget* widget : widgets)
{
GtkStyleContext* context = gtk_widget_get_style_context(widget);
const char* typeName = g_type_name_from_instance((GTypeInstance*)widget);
printf(" invalidating %p %s\n", context, typeName);
char css[1024];
sprintf(css, "* {background-color: #%02X0000}", g_numInvalidates % 0xFF);
GtkCssProvider* provider = gtk_css_provider_new();
gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
gtk_css_provider_load_from_data(provider, css, -1, 0);
gtk_style_context_invalidate(context);
}
}
int main (int argc, char **argv)
{
gtk_init(&argc, &argv);
g_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(g_window, "delete_event", gtk_main_quit, NULL);
gtk_window_set_default_size(GTK_WINDOW(g_window), 300, 200);
g_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(g_window), g_box);
g_scrolled = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add(GTK_CONTAINER(g_box), g_scrolled);
g_signal_connect(g_scrolled, "size-allocate", G_CALLBACK(on_size_allocate), NULL);
g_tree = gtk_tree_view_new();
gtk_container_add(GTK_CONTAINER(g_scrolled), g_tree);
GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes(
"Column", gtk_cell_renderer_text_new(),
"text", 0,
NULL
);
gtk_tree_view_append_column(GTK_TREE_VIEW(g_tree), column);
GtkTreeStore* treestore = gtk_tree_store_new(1, G_TYPE_STRING);
gtk_tree_view_set_model(GTK_TREE_VIEW(g_tree), GTK_TREE_MODEL(treestore));
GtkTreeIter iter;
gtk_tree_store_append(treestore, &iter, NULL);
gtk_tree_store_set(treestore, &iter, 0, "item", -1);
gtk_widget_show_all(g_window);
gtk_main();
return 0;
}
I added additional debug printing to GTK, and obtained this output:
on_size_allocate 0x55a2c97d0500
invalidating 0x55a2c94c4cf0 GtkBox
invalidating 0x55a2c96aaa20 GtkTreeView
gtk_css_gadget_allocate: style=0x55a2c97d6910(refcount 4 -> 2) current_style=0x55a2c97cf9b0
on_size_allocate 0x55a2c97d0500
invalidating 0x55a2c94c4cf0 GtkBox
invalidating 0x55a2c96aaa20 GtkTreeView
gtk_css_gadget_allocate: style=0x55a2c97cf9b0(refcount 1 -> 0) current_style=0x55a2c97d9750
Segmentation fault (core dumped)
Here it can be seen that on 2nd iteration of on_size_allocate
, it ends up looking into style
that was freed (0 refcount).
Version information
GTK 3.24.33-1ubuntu2 Ubuntu 22.04, default theme
Backtrace
#0 0x00007ffff7788d12 in gtk_css_style_get_value (id=16, style=0x555555a29a20) at ../../../../gtk/gtkcssstyle.c:74
#1 gtk_css_gadget_allocate(gadget=0x5555559ce4a0, allocation=0x7fffffffd430, baseline=-1, out_clip=0x7fffffffd360) at ../../../../gtk/gtkcssgadget.c:792
#2 0x00007ffff77327e9 in gtk_box_size_allocate (widget=0x5555558fc1b0, allocation=0x7fffffffd430) at ../../../../gtk/gtkbox.c:1225
#3 0x00007ffff799ee20 in gtk_widget_size_allocate_with_baseline(widget=0x5555558fc1b0, allocation=<optimized out>, baseline=<optimized out>) at ../../../../gtk/gtkwidget.c:6179
#4 0x00007ffff79bb78b in gtk_window_size_allocate (widget=0x555555728260, allocation=<optimized out>) at ../../../../gtk/gtkwindow.c:8071
#5 0x00007ffff799ee20 in gtk_widget_size_allocate_with_baseline(widget=0x555555728260, allocation=<optimized out>, baseline=<optimized out>) at ../../../../gtk/gtkwidget.c:6179
#6 0x00007ffff79c013d in gtk_window_move_resize (window=0x555555728260) at ../../../../gtk/gtkwindow.c:10300
#7 0x00007ffff7f71700 in _g_closure_invoke_va (param_types=0x0, n_params=0, args=0x7fffffffd850, instance=0x555555728260, return_value=<optimized out>, closure=<optimized out>) at ../../../gobject/gclosure.c:893
#8 g_signal_emit_valist (instance=0x555555728260, signal_id=109, detail=<optimized out>, var_args=var_args@entry=0x7fffffffd850) at ../../../gobject/gsignal.c:3440
#9 0x00007ffff7f71863 in g_signal_emit (instance=<optimized out>, signal_id=<optimized out>, detail=<optimized out>) at ../../../gobject/gsignal.c:3587
#10 0x00007ffff79bc2f3 in gtk_window_show (widget=0x555555728260) at ../../../../gtk/gtkwindow.c:6321
#14 0x00007ffff7f71863 in <emit signal ??? on instance ???> (instance=instance@entry=0x555555728260, signal_id=<optimized out>, detail=detail@entry=0) at ../../../gobject/gsignal.c:3587
#11 0x00007ffff7f53d2f in g_closure_invoke (closure=0x5555555aae00, return_value=0x0, n_param_values=1, param_values=0x7fffffffdae0, invocation_hint=0x7fffffffda60) at ../../../gobject/gclosure.c:830
#12 0x00007ffff7f6f895 in signal_emit_unlocked_R (node=node@entry=0x5555555aae50, detail=detail@entry=0, instance=instance@entry=0x555555728260, emission_return=emission_return@entry=0x0, instance_and_params=instance_and_params@entry=0x7fffffffdae0) at ../../../gobject/gsignal.c:3707
#13 0x00007ffff7f71614 in g_signal_emit_valist (instance=<optimized out>, signal_id=<optimized out>, detail=<optimized out>, var_args=var_args@entry=0x7fffffffdc90) at ../../../gobject/gsignal.c:3530
#15 0x00007ffff799e656 in gtk_widget_show (widget=0x555555728260) at ../../../../gtk/gtkwidget.c:4852
#16 0x00005555555559bd in main(int, char**) (argc=1, argv=0x7fffffffdf08) at SWT_Issue0697_crash_gtk_cssgadget_allocate.cpp:72
Edited by SyntevoAlex