Crash during drag-and-drop in flowbox in debug mode
@ruslanizhb
Submitted by LRN Assigned to gtk-win32 maintainers
Link to original bug (#785167)
Description
Created attachment 356023 testcase
GTK crashes when finishing an attempted drag-and-drop operation (i.e. releasing the left mouse button).
How to reproduce:
- Compile the attached testcase
- Run it under gdb
It will crash with this backtrace:
#0 gdk_event_free (event=0xfeeefeee) at gdkevents.c:772
#1 0x66971cd7 in gtk_drag_source_info_destroy (info=info@entry=0x39e0568) at gtkdnd.c:2817
#2 0x66973512 in gtk_drag_drop_finished (info=info@entry=0x39e0568, result=result@entry=GTK_DRAG_RESULT_NO_TARGET, time=<optimized out>) at gtkdnd.c:2593
#3 0x66973727 in gtk_drag_cancel_internal (info=info@entry=0x39e0568, result=result@entry=GTK_DRAG_RESULT_NO_TARGET, time=<optimized out>) at gtkdnd.c:2981
#4 0x66973a2f in gtk_drag_button_release_cb (widget=0x3a71280, event=0x3a6d3e8, data=0x39e0568) at gtkdnd.c:3238
#5 0x667d9380 in _gtk_marshal_BOOLEAN__BOXED (closure=0x39df960, return_value=0x61fa28, n_param_values=2, param_values=0x61faa0, invocation_hint=0x61fa4c, marshal_data=0x0) at gtkmarshalers.c:82
#6 0x63c463b6 in g_closure_invoke (closure=0x39df960, return_value=return_value@entry=0x61fa28, n_param_values=n_param_values@entry=2, param_values=param_values@entry=0x61faa0,
invocation_hint=invocation_hint@entry=0x61fa4c) at gclosure.c:804
#7 0x63c59801 in signal_emit_unlocked_R (node=node@entry=0x2715168, detail=0, instance=0x3a71280, emission_return=emission_return@entry=0x61fb30, instance_and_params=0x61faa0) at gsignal.c:3635
#8 0x63c61a80 in g_signal_emit_valist (instance=instance@entry=0x3a71280, signal_id=signal_id@entry=80, detail=detail@entry=0, var_args=<optimized out>, var_args@entry=0x61fbdc "eO│\003\034ua")
at gsignal.c:3401
#9 0x63c61fb6 in g_signal_emit (instance=instance@entry=0x3a71280, signal_id=80, detail=detail@entry=0) at gsignal.c:3447
#10 0x66941803 in gtk_widget_event_internal (widget=0x3a71280, event=0x3a6d3e8) at gtkwidget.c:7723
#11 0x667d6082 in propagate_event_up (topmost=<optimized out>, event=<optimized out>, widget=0x3a71280) at gtkmain.c:2568
#12 propagate_event (widget=<optimized out>, event=0x3a6d3e8, captured=<optimized out>, topmost=0x0) at gtkmain.c:2670
#13 0x667d824d in gtk_main_do_event (event=<optimized out>) at gtkmain.c:1901
#14 0x710cdf5e in _gdk_event_emit (event=event@entry=0x3a6d3e8) at gdkevents.c:73
#15 0x710fa481 in gdk_event_dispatch (source=source@entry=0x270dc88, callback=0x0, user_data=0x0) at gdkevents-win32.c:3965
#16 0x687f6108 in g_main_dispatch (context=0x26d3678) at gmain.c:3148
#17 g_main_context_dispatch (context=context@entry=0x26d3678) at gmain.c:3813
#18 0x687f63e8 in g_main_context_iterate (context=context@entry=0x26d3678, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3886
#19 0x687f66aa in g_main_context_iteration (context=context@entry=0x26d3678, may_block=may_block@entry=1) at gmain.c:3947
#20 0x6d7f4d61 in g_application_run (application=0x26ca0b0, argc=1, argv=0xf1a798) at gapplication.c:2378
#21 0x004016bb in main ()
I suspect that this code in gtkdnd.c is responsible:
g_object_set_data_full (G_OBJECT (info->context), "former-gtk-source-info", info, (GDestroyNotify)gtk_drag_source_info_free);
gtk_drag_clear_source_info (info->context); g_object_unref (info->context);
if (info->last_event) gdk_event_free (info->last_event);
What appears to happen is that gtk attaches the pointer to the dragsourceinfo that is in process of destruction to the context object, then unrefs (and potentially destroys) the context object. While the context object is being destroyed, glib dutifully cleans up all the data attached to it - including the dragsourceinfo. Once the context is destroyed, the code goes back to destroying dragsourceinfo (which is freed already at this point). Due to freed memory now containing garbage (apparently, msvcrt free() likes poisoning memory), the code is tricked into calling gdk_event_free() on a bad pointer, and it goes downhill from here.
I do not understand the logic behind this code. I only have some guesses as to why it doesn't crash on other platforms, and why it doesn't crash when not debugging:
- msvcrt only poisons memory in debug mode. When not running under gdb, info->last_event is still 0 (as the memory was freed recently and had no chance to be re-used for something else), and the luckily dodges a crash
- the code above assumes that other references to the context object exist, and on other platforms this is so, therefore the object is not destroyed right there, and survives until the end of the function.
Suggested fix: No idea. Moving g_object_unref (info->context) to the end of the function might do the trick, but i'm not sure how correct that is.
Attachment 356023, "testcase":
flowtest.c
Version: 3.22.x