After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 605743 - gtk_enumerate_printers may crash (SIGSEGV) or hang
gtk_enumerate_printers may crash (SIGSEGV) or hang
Status: RESOLVED DUPLICATE of bug 686838
Product: gtk+
Classification: Platform
Component: Printing
2.19.x
Other Linux
: Normal major
: ---
Assigned To: gtk-bugs
gtk-bugs
Depends on:
Blocks:
 
 
Reported: 2009-12-30 14:21 UTC by Rafal Luzynski
Modified: 2015-03-13 13:55 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
The patch that should fix the problem (5.45 KB, patch)
2010-01-04 23:44 UTC, Rafal Luzynski
none Details | Review

Description Rafal Luzynski 2009-12-30 14:21:27 UTC
This bug is difficult to track because it occurs very rarely on most systems but on one of my machines it occurs almost every time. Here are some details about the machine:

- the Linux distribution is OpenSUSE 11.1,
- there is only one real (not 'Print to File') printer configured in the system, it is a network printer shared via Samba and it is the default printer,
- the CPU is a single-core AMD Athlon XP 2200+ (actual CPU clock is 1800 MHz),
- the application is multithreaded,
- the application is running in the remote X session (via ssh) but the problem may also occur when running on the local display.

Other machines have multicore CPUs, multiple printers, and use Fedora.

Here is my investigation trying to explain what happens. The reason of the bug is in the following lines of gtk_enumerate_printers (the line numbers are from the latest version of gtkprinter.c):

1089 static void
1090 free_printer_list (PrinterList *printer_list)
1091 {
1092   if (printer_list->destroy)
1093     printer_list->destroy (printer_list->data);
1094 
1095   if (printer_list->loop)
1096     {
1097       g_main_loop_quit (printer_list->loop);
1098       g_main_loop_unref (printer_list->loop);
1099     }
1100 
1101   g_free (printer_list);
1102 }
/* ... */
1208 void
1209 gtk_enumerate_printers (GtkPrinterFunc func,
1210 			gpointer       data,
1211 			GDestroyNotify destroy,
1212 			gboolean       wait)
1213 {
1214   PrinterList *printer_list;
/* ... */
1241   if (wait && printer_list->backends)
1242     {
1243       printer_list->loop = g_main_loop_new (NULL, FALSE);
1244 
1245       GDK_THREADS_LEAVE ();
1246       g_main_loop_run (printer_list->loop);
1247       GDK_THREADS_ENTER ();
1248     }

gtk_enumerate_printers calls list_printers_init for each print backend (line 1237, not listed here); list_printers_init installs the signal handlers list_added_cb and list_done_cb if the backend which is neither unavailable nor has the printer list completed (lines 1181-1186); list_done_cb calls free_printer_list when all backends are processed (line 1147); free_printer_list quits the loop and frees the printer_list. Also list_added_cb may call stop_enumeration which calls list_done_cb for all backends.

On my machine all printer data are almost always delivered between the lines 1245 and 1246, that is after leaving GDK threads and before running the inner main loop. This means that printer_list and printer_list->loop are initialized correctly but when g_main_loop_run() is called printer_list is already freed and the pointer contains garbage. The main thread is not notified that it should not run the loop. Also free_printer_list is not told that the loop has not yet been started but this does not cause any problem.

The result depends on the GTK version currently used. I have copied the original code of gtk_enumerate_printers, added some debug messages and tested with different GTK versions. With GTK 2.14.4 and 2.16.1 the log is:

 1 Hello from _gtk_enumerate_printers, wait==TRUE
 2 Hello from list_printers_init
 3 printer_list=0x8244f80, printer_list->loop=(nil), ->loop->context=(nil)
 4 Hello from list_added_cb
 5 printer_list=0x8244f80, printer_list->loop=(nil), ->loop->context=(nil)
 6 Adding the printer Print to File
 7 Hello from list_printers_init
 8 printer_list=0x8244f80, printer_list->loop=(nil), ->loop->context=(nil)
 9 _gtk_enumerate_printers created the loop 0x834bb40 with context 0x81361c0
10 Hello from list_added_cb
11 printer_list=0x8244f80, printer_list->loop=0x834bb40, ->loop->context=0x81361c0
12 Adding the printer HP-LaserJet-4-Plus
13 Hello from stop_enumeration
14 printer_list=0x8244f80, printer_list->loop=0x834bb40, ->loop->context=0x81361c0
15 Hello from list_done_cb
16 printer_list=0x8244f80, printer_list->loop=0x834bb40, ->loop->context=0x81361c0
17 Hello from free_printer_list
18 printer_list=0x8244f80, printer_list->loop=0x834bb40, ->loop->context=0x81361c0
19 _gtk_enumerate_printers will run the loop 0x834bb40 with context (nil)
20 SIGSEGV caught, value=11
21 Stack depth: 15
22 	/home/rl/xxxx/xxxx [0x806f7a1]
23 	[0xffffe400]
24 	/lib/libpthread.so.0(pthread_mutex_lock+0x20) [0xb76fc560]
25 	/usr/lib/libglib-2.0.so.0(g_main_loop_run+0x7f) [0xb76671ff]
26 	...

In this log the line 9 is printed between the lines 1243 and 1245 of the original source: the inner main loop has been created and the application is about to leave the GDK threads. The line 19 is printed between the lines 1245 and 1246 of the original source: the application has left the GDK threads and is about to run the inner main loop. As you can see from the log the loop has already been freed but there is no reliable way to tell this because printer_list is freed, too, and it is bad idea to check the contents of the freed memory area.

With GTK 2.18.1, 2.18.5, 2.19.0 and 2.19.2 the result is even worse:

 1 Hello from _gtk_enumerate_printers, wait==TRUE
 2 Hello from list_printers_init
 3 printer_list=0x8246560, printer_list->loop=(nil), ->loop->context=(nil)
 4 Hello from list_added_cb
 5 printer_list=0x8246560, printer_list->loop=(nil), ->loop->context=(nil)
 6 Adding the printer Print to File
 7 Hello from list_printers_init
 8 printer_list=0x8246560, printer_list->loop=(nil), ->loop->context=(nil)
 9 _gtk_enumerate_printers created the loop 0x83517e0 with context 0x8137288
10 Hello from list_added_cb
11 printer_list=0x8246560, printer_list->loop=0x83517e0, ->loop->context=0x8137288
12 Adding the printer HP-LaserJet-4-Plus
13 Hello from stop_enumeration
14 printer_list=0x8246560, printer_list->loop=0x83517e0, ->loop->context=0x8137288
15 Hello from list_done_cb
16 printer_list=0x8246560, printer_list->loop=0x83517e0, ->loop->context=0x8137288
17 Hello from free_printer_list
18 printer_list=0x8246560, printer_list->loop=0x83517e0, ->loop->context=0x8137288
19 _gtk_enumerate_printers will run the loop 0x83517e0 with context 0x8369de8
20 *** glibc detected *** /home/rl/xxxx/xxxx: corrupted double-linked list: 0x08369de8 ***
21 ======= Backtrace: =========
22 /lib/libc.so.6[0xb6f9e654]
23 /lib/libc.so.6[0xb6f9e8de]
24 /lib/libc.so.6[0xb6fa0845]
25 /lib/libc.so.6[0xb6fa1751]
26 /lib/libc.so.6(__libc_memalign+0xec)[0xb6fa2abc]
27 /lib/libc.so.6(posix_memalign+0x87)[0xb6fa2c97]
28 /usr/lib/libglib-2.0.so.0[0xb77c64f1]
29 /usr/lib/libglib-2.0.so.0(g_slice_alloc+0x646)[0xb77c7466]
30 /usr/lib/libglib-2.0.so.0(g_slice_alloc0+0x26)[0xb77c7606]
31 /usr/lib/libgobject-2.0.so.0(g_type_create_instance+0x9e)[0xb7755a7e]
32 /usr/lib/libgobject-2.0.so.0[0xb7739292]
33 /usr/lib/libgobject-2.0.so.0(g_object_newv+0xe2)[0xb773a262]
34 /usr/lib/libgobject-2.0.so.0(g_object_new_valist+0x2dd)[0xb773b08d]
35 /usr/lib/libgobject-2.0.so.0(g_object_new+0x70)[0xb773b210]
36 /usr/lib/libgtk-x11-2.0.so.0[0xb752314c]
37 /usr/lib/libgtk-x11-2.0.so.0(gtk_main_do_event+0x1cd)[0xb7440fed]
38 /usr/lib/libgdk-x11-2.0.so.0[0xb72b709a]
39 /usr/lib/libglib-2.0.so.0(g_main_context_dispatch+0x212)[0xb77a8612]
40 /usr/lib/libglib-2.0.so.0[0xb77abee8]
41 /usr/lib/libglib-2.0.so.0(g_main_loop_run+0x1bf)[0xb77ac33f]
42 /usr/lib/libgtk-x11-2.0.so.0(gtk_main+0xb9)[0xb74415c9]
43 /home/rl/xxxx/xxxx[0x806e820]
44 /lib/libpthread.so.0[0xb78401b5]
46 /lib/libc.so.6(clone+0x5e)[0xb70033ae]
47 ======= Memory map: ========
[ cut ]

The difference is that the application hangs after the log line 19 and then crashes generating the rest of the log when I move the mouse over its window. In the log one can see that printer_list->loop not only contains garbage but also has been already reused because context contains the new value 0x8369de8 (the old and correct was 0x8137288).

Probably there are also other reasons of my problem, like bad synchronization of the threads, but this example shows that gtk_enumerate_printers is vulnerable in some cases when the print backends are "too fast."

For now I have no idea what should be the correct solution of the bug. The problem may be related with bug 346903 and bug 434318 but they are already fixed and committed and my problem still persists. Maybe printer_list should count the references and free only when the last reference is released. I was thinking about freeing printer_list in gtk_enumerate_printers rather than in free_printer_list but it crashed when wait==FALSE and the loop is never run but the backends still provide the printer data after leaving gtk_enumerate_printers. The attachment 87229 [details] [review] for the bug 434318 with its maybe_quit() function may be a good hint.

I have tested this with the latest GLib 2.23.1 and the original 2.18.2, upgrading does not help.
Comment 1 Rafal Luzynski 2010-01-04 23:44:51 UTC
Created attachment 150806 [details] [review]
The patch that should fix the problem

Works correctly on my test machine. Any comments are welcome.
Comment 2 Rafal Luzynski 2010-03-16 15:16:23 UTC
Any comments about it? Hasn't anybody experienced any unexplained GTK crashes when accessing a printer?
Comment 3 Morten Welinder 2015-03-13 13:55:04 UTC
This is the same problem as bug 686838 which has a simpler patch.
(Also years old and unapplied.)

*** This bug has been marked as a duplicate of bug 686838 ***