base-manager: fix segfault when using already disposed MMDevice

Since commit e9d0989ed0, the MMDevice may be removed from the
tracking hash table when the support check operation fails to create a
modem object.

If this failure happens due to the port probe cancellations requested
during the udev removal event for a given device port, we would end up
using an already disposed object and triggering a segfault.

This fix just makes sure a full valid reference to the MMDevice object
is kept around until we're done using it.

     [mm-base-manager.c:216] device_removed(): (usbmisc/cdc-wdm1): released by device '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.4'
     [mm-plugin-manager.c:1131] device_context_port_released(): [plugin manager] task 5: port released: cdc-wdm1
     [mm-base-manager.c:216] device_removed(): (tty/ttyACM0): released by device '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.4'
     [mm-plugin-manager.c:1131] device_context_port_released(): [plugin manager] task 5: port released: ttyACM0
     [mm-base-manager.c:221] device_removed(): Removing empty device '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.4'
     [mm-plugin-manager.c:1219] device_context_cancel(): [plugin manager) task 5: cancellation requested
     [mm-plugin-manager.c:979] device_context_continue(): [plugin manager] task 5: no more ports to probe
     [mm-plugin-manager.c:813] device_context_complete(): [plugin manager] task 5: finished in '0.090510' seconds
     [mm-base-manager.c:172] device_support_check_ready(): Couldn't check support for device '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.4': Operation was cancelled
     [mm-base-manager.c:223] device_removed(): Device support check has been cancelled

    Thread 1 "ModemManager" received signal SIGSEGV, Segmentation fault.
    0x00007ffff6543c50 in g_str_hash () from /usr/lib/libglib-2.0.so.0
    (gdb) bt
    #0  0x00007ffff6543c50 in g_str_hash () at /usr/lib/libglib-2.0.so.0
    #1  0x00007ffff6542b2d in  () at /usr/lib/libglib-2.0.so.0
    #2  0x0000000000439675 in device_removed (self=0x770900, kernel_device=0x763e60) at mm-base-manager.c:225
    #3  0x0000000000439e70 in handle_uevent (client=0x769c20, action=0x81d910 "remove", device=0x7fffe4001c40, user_data=0x770900) at mm-base-manager.c:415
    #4  0x00007ffff54c61c8 in ffi_call_unix64 () at /usr/lib/libffi.so.6
    #5  0x00007ffff54c5c2a in ffi_call () at /usr/lib/libffi.so.6
    #6  0x00007ffff682d7ae in g_cclosure_marshal_generic ()
        at /usr/lib/libgobject-2.0.so.0
    #7  0x00007ffff682cf75 in g_closure_invoke () at /usr/lib/libgobject-2.0.so.0
    #8  0x00007ffff683ef82 in  () at /usr/lib/libgobject-2.0.so.0
    #9  0x00007ffff6847bcc in g_signal_emit_valist ()
        at /usr/lib/libgobject-2.0.so.0
    #10 0x00007ffff6847faf in g_signal_emit () at /usr/lib/libgobject-2.0.so.0
    #11 0x00007ffff7023c74 in  () at /usr/lib/libgudev-1.0.so.0
    #12 0x00007ffff655445a in g_main_context_dispatch ()
        at /usr/lib/libglib-2.0.so.0
    #13 0x00007ffff6554810 in  () at /usr/lib/libglib-2.0.so.0
    #14 0x00007ffff6554b32 in g_main_loop_run () at /usr/lib/libglib-2.0.so.0
    #15 0x0000000000437bf5 in main (argc=2, argv=0x7fffffffeb28) at
    main.c:180

    (gdb) fr 2
    #2  0x0000000000439675 in device_removed (self=0x770900, kernel_device=0x763e60) at mm-base-manager.c:225
    225	                g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));

    (gdb) p mm_device_get_uid (device)
    $1 = (const gchar *) 0x0

    (gdb) p *device
    $3 = {parent = {g_type_instance = {g_class = 0x0}, ref_count = 0, qdata = 0x0}, priv = 0x7feb20}
This commit is contained in:
Aleksander Morgado
2017-03-30 21:14:46 +02:00
parent 27e4c74c60
commit 822bfa4ca1

View File

@@ -218,11 +218,22 @@ device_removed (MMBaseManager *self,
/* If port probe list gets empty, remove the device object iself */ /* If port probe list gets empty, remove the device object iself */
if (!mm_device_peek_port_probe_list (device)) { if (!mm_device_peek_port_probe_list (device)) {
/* The callback triggered when the device support is cancelled may end up
* removing the device from the HT, and that was the last full reference
* we kept. So, in order to make sure the reference is still valid after
* support_check_cancel(), we hold a full reference ourselves. */
mm_dbg ("Removing empty device '%s'", mm_device_get_uid (device)); mm_dbg ("Removing empty device '%s'", mm_device_get_uid (device));
if (mm_plugin_manager_device_support_check_cancel (self->priv->plugin_manager, device)) g_object_ref (device);
mm_dbg ("Device support check has been cancelled"); {
mm_device_remove_modem (device); if (mm_plugin_manager_device_support_check_cancel (self->priv->plugin_manager, device))
g_hash_table_remove (self->priv->devices, mm_device_get_uid (device)); mm_dbg ("Device support check has been cancelled");
/* The device may have already been removed from the tracking HT, we
* just try to remove it and if it fails, we ignore it */
mm_device_remove_modem (device);
g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
}
g_object_unref (device);
} }
} }