diff options
author | Ondrej Holy <oholy@redhat.com> | 2018-04-10 13:17:38 +0200 |
---|---|---|
committer | Ondrej Holy <oholy@redhat.com> | 2018-04-13 10:18:13 +0200 |
commit | d35bab6b2fa48db93b571d599ff2f256a213b169 (patch) | |
tree | 1c07992090f7488732fca5561ed9e1e7a814a539 | |
parent | d7e1397854f32e793b4f65d894908d67072dcb3f (diff) | |
download | gvfs-d35bab6b2fa48db93b571d599ff2f256a213b169.tar.gz |
daemon: Prevent deadlock and invalid read when closing channels
Commit e147e48 added missing mutex guards for job_sources, which may
unfortunately lead to deadlock because g_vfs_channel_force_close
synchronously calls g_vfs_job_source_closed which is also guarded by
the same mutex.
The deadlock reveals another bug which was in that code. The code
iterates over job_sources list, but g_vfs_job_source_closed removes
current element of the list, which leads to invalid reads and
potentially to segfaults also.
This patch tries to fix the both mentioned issues.
https://bugzilla.gnome.org/show_bug.cgi?id=794957
-rw-r--r-- | daemon/gvfsdaemon.c | 28 |
1 files changed, 22 insertions, 6 deletions
diff --git a/daemon/gvfsdaemon.c b/daemon/gvfsdaemon.c index 1f4849ad..406d4f8e 100644 --- a/daemon/gvfsdaemon.c +++ b/daemon/gvfsdaemon.c @@ -1126,13 +1126,29 @@ g_vfs_daemon_close_active_channels (GVfsDaemon *daemon, GVfsBackend *backend) { GList *l; + GVfsChannel *channel_to_close; - g_mutex_lock (&daemon->lock); + do + { + channel_to_close = NULL; - for (l = daemon->job_sources; l != NULL; l = l->next) - if (G_VFS_IS_CHANNEL (l->data) && - g_vfs_channel_get_backend (G_VFS_CHANNEL (l->data)) == backend) - g_vfs_channel_force_close (G_VFS_CHANNEL (l->data)); + g_mutex_lock (&daemon->lock); + for (l = daemon->job_sources; l != NULL; l = l->next) + { + if (G_VFS_IS_CHANNEL (l->data) && + g_vfs_channel_get_backend (G_VFS_CHANNEL (l->data)) == backend) + { + channel_to_close = g_object_ref (G_VFS_CHANNEL (l->data)); + break; + } + } + g_mutex_unlock (&daemon->lock); - g_mutex_unlock (&daemon->lock); + if (channel_to_close) + { + g_vfs_channel_force_close (channel_to_close); + g_object_unref (channel_to_close); + } + } + while (channel_to_close != NULL); } |