[Spice-devel] [PATCH spice-server 2/4] red-client: Protect concurrent list accesses
Frediano Ziglio
fziglio at redhat.com
Wed Aug 30 12:51:26 UTC 2017
The channels list was not protected by a lock in red_client_destroy.
This could cause for instance a RedChannelClient object to be
created while scanning the list so potentially modifying the
list while scanning with all race issues.
Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
server/red-client.c | 29 ++++++++++++++++++++++++-----
1 file changed, 24 insertions(+), 5 deletions(-)
diff --git a/server/red-client.c b/server/red-client.c
index ddfc5400..800b1a5d 100644
--- a/server/red-client.c
+++ b/server/red-client.c
@@ -192,9 +192,6 @@ void red_client_migrate(RedClient *client)
void red_client_destroy(RedClient *client)
{
- RedChannelClient *rcc;
-
- spice_printerr("destroy client %p with #channels=%d", client, g_list_length(client->channels));
if (!pthread_equal(pthread_self(), client->thread_id)) {
spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
"If one of the threads is != io-thread && != vcpu-thread,"
@@ -202,23 +199,45 @@ void red_client_destroy(RedClient *client)
client->thread_id,
pthread_self());
}
- red_client_set_disconnecting(client);
- FOREACH_CHANNEL_CLIENT(client, rcc) {
+
+ pthread_mutex_lock(&client->lock);
+ spice_printerr("destroy client %p with #channels=%d", client, g_list_length(client->channels));
+ // this make sure that possible RedChannelClient setting up
+ // to be added won't be added to the list
+ client->disconnecting = TRUE;
+ while (client->channels) {
RedChannel *channel;
+ RedChannelClient *rcc = client->channels->data;
+
+ // Remove the list to the RedChannelClient we are processing
+ // note that we own the object so is safe to do some operations on it.
+ // This manual scan of the list is done to have a thread safe
+ // iteration of the list
+ client->channels = g_list_delete_link(client->channels, client->channels);
+
// some channels may be in other threads, so disconnection
// is not synchronous.
channel = red_channel_client_get_channel(rcc);
red_channel_client_set_destroying(rcc);
+
+ // prevent dead lock disconnecting rcc (which can happen
+ // in the same thread or synchronously on another one)
+ pthread_mutex_unlock(&client->lock);
+
// some channels may be in other threads. However we currently
// assume disconnect is synchronous (we changed the dispatcher
// to wait for disconnection)
// TODO: should we go back to async. For this we need to use
// ref count for channel clients.
red_channel_disconnect_client(channel, rcc);
+
spice_assert(red_channel_client_pipe_is_empty(rcc));
spice_assert(red_channel_client_no_item_being_sent(rcc));
+
red_channel_client_destroy(rcc);
+ pthread_mutex_lock(&client->lock);
}
+ pthread_mutex_unlock(&client->lock);
g_object_unref(client);
}
--
2.13.5
More information about the Spice-devel
mailing list