[Spice-devel] [spice-gtk v3 3/4] usb-channel: Really stop listening for USB events on disconnection
Christophe Fergeau
cfergeau at redhat.com
Thu Jun 30 13:40:02 UTC 2016
When using USB redirection, it's fairly easy to leak the thread handling
USB events, which will eventually cause problems in long lived apps.
In particular, in virt-manager, one can:
- start a VM
- connect to it with SPICE
- open the USB redirection window
- redirect a device
- close the SPICE window
-> the SpiceUsbDeviceManager instance will be destroyed (including the
USB context it owns), but the associated event thread will keep running.
Since it's running a loop blocking on libusb_handle_events(priv->context),
the loop will eventually try to use the USB context we just destroyed
causing a crash.
We can get in this situation when redirecting a USB device because we
will call spice_usb_device_manager_start_event_listening() in
spice_usbredir_channel_open_device(). The matching
spice_usb_device_manager_stop_event_listening() call is supposed to
happen in spice_usbredir_channel_disconnect_device(), however by the
time it's called in the scenario described above, the session associated
with the channel will already have been set to NULL in
spice_session_channel_destroy().
The session is only needed in order to get the SpiceUsbDeviceManager
instance we need to call spice_usb_device_manager_stop_event_listening()
on. This patch stores it in SpiceChannelUsbredir instead, this way we
don't need SpiceChannel::session to be non-NULL during device
disconnection.
This should fix the issues described in
https://bugzilla.redhat.com/show_bug.cgi?id=1217202
(virt-manager) and most
likely https://bugzilla.redhat.com/show_bug.cgi?id=1337007 (gnome-boxes)
as well.
---
src/channel-usbredir.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index df18be2..6595508 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -79,6 +79,7 @@ struct _SpiceUsbredirChannelPrivate {
SpiceUsbAclHelper *acl_helper;
#endif
GMutex device_connect_mutex;
+ SpiceUsbDeviceManager *usb_device_manager;
};
static void channel_set_handlers(SpiceChannelClass *klass);
@@ -188,6 +189,11 @@ static void spice_usbredir_channel_dispose(GObject *obj)
SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
spice_usbredir_channel_disconnect_device(channel);
+ /* This should have been set to NULL during device disconnection,
+ * but better not to leak it if this does not happen for some reason
+ */
+ g_warn_if_fail(channel->priv->usb_device_manager == NULL);
+ g_clear_object(&channel->priv->usb_device_manager);
/* Chain up to the parent class */
if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->dispose)
@@ -275,6 +281,7 @@ static gboolean spice_usbredir_channel_open_device(
SpiceUsbredirChannel *channel, GError **err)
{
SpiceUsbredirChannelPrivate *priv = channel->priv;
+ SpiceSession *session;
libusb_device_handle *handle = NULL;
int rc, status;
@@ -300,10 +307,9 @@ static gboolean spice_usbredir_channel_open_device(
return FALSE;
}
- if (!spice_usb_device_manager_start_event_listening(
- spice_usb_device_manager_get(
- spice_channel_get_session(SPICE_CHANNEL(channel)), NULL),
- err)) {
+ session = spice_channel_get_session(SPICE_CHANNEL(channel));
+ priv->usb_device_manager = g_object_ref(spice_usb_device_manager_get(session, NULL));
+ if (!spice_usb_device_manager_start_event_listening(priv->usb_device_manager, err)) {
usbredirhost_set_device(priv->host, NULL);
return FALSE;
}
@@ -481,12 +487,10 @@ void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
* usbredirhost_set_device NULL will interrupt the
* libusb_handle_events call in the thread.
*/
- {
- SpiceSession *session = spice_channel_get_session(SPICE_CHANNEL(channel));
- if (session != NULL)
- spice_usb_device_manager_stop_event_listening(
- spice_usb_device_manager_get(session, NULL));
- }
+ g_warn_if_fail(priv->usb_device_manager != NULL);
+ spice_usb_device_manager_stop_event_listening(priv->usb_device_manager);
+ g_clear_object(&priv->usb_device_manager);
+
/* This also closes the libusb handle we passed from open_device */
usbredirhost_set_device(priv->host, NULL);
g_clear_pointer(&priv->device, libusb_unref_device);
--
2.7.4
More information about the Spice-devel
mailing list