[Spice-commits] 2 commits - server/red-channel-client.c server/red-channel-client.h server/spicevmc.c

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Tue Sep 24 12:38:23 UTC 2019


 server/red-channel-client.c |   28 ++++++++++++++++++++++++++++
 server/red-channel-client.h |    4 ++++
 server/spicevmc.c           |   26 +++++++++++++++++---------
 3 files changed, 49 insertions(+), 9 deletions(-)

New commits:
commit 01de3b89229c3ee469a837c2f845500714445cd1
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Mon Jun 17 16:24:15 2019 +0100

    spicevmc: Avoids DoS if guest device is not able to get data faster enough
    
    This fix half (one direction) of
    https://gitlab.freedesktop.org/spice/spice/issues/29.
    Specifically if you have attempt to transfer a file from the client
    using WebDAV.
    Previously the queue to the device was unbound. If device was not
    getting data fast enough the server started queuing data.
    To easily test this you can suspend the WebDAV daemon while transferring
    a big file from the client.
    To simplify the code and reduce the changes server buffers are
    used. This as client token implementation is written with agent
    in mind. While when we exhaust server tokens RedCharDevice doesn't
    close the client when we exhaust client tokens RedCharDevice closes
    the client which in this case it's not wanted.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Victor Toso <victortoso at redhat.com>

diff --git a/server/spicevmc.c b/server/spicevmc.c
index 619f08cb..f01d5416 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -491,9 +491,9 @@ static bool handle_compressed_msg(RedVmcChannel *channel, RedChannelClient *rcc,
     int decompressed_size;
     RedCharDeviceWriteBuffer *write_buf;
 
-    write_buf = red_char_device_write_buffer_get_client(channel->chardev,
-                                                        red_channel_client_get_client(rcc),
-                                                        compressed_data_msg->uncompressed_size);
+    write_buf = red_char_device_write_buffer_get_server(channel->chardev,
+                                                        compressed_data_msg->uncompressed_size,
+                                                        false);
     if (!write_buf) {
         return FALSE;
     }
@@ -565,6 +565,15 @@ static bool spicevmc_red_channel_client_handle_message(RedChannelClient *rcc,
     return TRUE;
 }
 
+/* if device manage to send some data attempt to unblock the channel */
+static void spicevmc_on_free_self_token(RedCharDevice *self)
+{
+    RedCharDeviceSpiceVmc *vmc = RED_CHAR_DEVICE_SPICEVMC(self);
+    RedVmcChannel *channel = RED_VMC_CHANNEL(vmc->channel);
+
+    red_channel_client_unblock_read(channel->rcc);
+}
+
 static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
                                                        uint16_t type,
                                                        uint32_t size)
@@ -572,16 +581,14 @@ static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
 
     switch (type) {
     case SPICE_MSGC_SPICEVMC_DATA: {
-        RedClient *client = red_channel_client_get_client(rcc);
         RedVmcChannel *channel = RED_VMC_CHANNEL(red_channel_client_get_channel(rcc));
 
         assert(!channel->recv_from_client_buf);
 
-        channel->recv_from_client_buf = red_char_device_write_buffer_get_client(channel->chardev,
-                                                                                client,
-                                                                                size);
+        channel->recv_from_client_buf = red_char_device_write_buffer_get_server(channel->chardev,
+                                                                                size, true);
         if (!channel->recv_from_client_buf) {
-            spice_error("failed to allocate write buffer");
+            red_channel_client_block_read(rcc);
             return NULL;
         }
         return channel->recv_from_client_buf->buf;
@@ -908,6 +915,7 @@ red_char_device_spicevmc_class_init(RedCharDeviceSpiceVmcClass *klass)
     char_dev_class->send_msg_to_client = spicevmc_chardev_send_msg_to_client;
     char_dev_class->remove_client = spicevmc_char_dev_remove_client;
     char_dev_class->port_event = spicevmc_port_event;
+    char_dev_class->on_free_self_token = spicevmc_on_free_self_token;
 
     g_object_class_install_property(object_class,
                                     PROP_CHANNEL,
@@ -934,7 +942,7 @@ red_char_device_spicevmc_new(SpiceCharDeviceInstance *sin,
                         "sin", sin,
                         "spice-server", reds,
                         "client-tokens-interval", 0ULL,
-                        "self-tokens", ~0ULL,
+                        "self-tokens", 128, // limit number of messages sent to device
                         "channel", channel,
                         NULL);
 }
commit 726b1824e33d27ff8a35bddc9b59b35bbeb90831
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jun 15 18:31:11 2019 +0100

    red-channel-client: Allows to block receiving data
    
    If the client is keeping sending data while we can't handle them
    (for instance because we need to forward to a device but the
    device is not fast enough to receive that amount of data) allows
    to stop RedChannelClient to read data.
    This after a bit will stop the client sending data as its output
    buffer will become full.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Victor Toso <victortoso at redhat.com>

diff --git a/server/red-channel-client.c b/server/red-channel-client.c
index 2fee76f5..66b8fd4a 100644
--- a/server/red-channel-client.c
+++ b/server/red-channel-client.c
@@ -145,6 +145,7 @@ struct RedChannelClientPrivate
         } urgent;
     } send_data;
 
+    bool block_read;
     bool during_send;
     GQueue pipe;
 
@@ -969,10 +970,32 @@ red_channel_client_watch_update_mask(RedChannelClient *rcc, int event_mask)
         return;
     }
 
+    if (rcc->priv->block_read) {
+        event_mask &= ~SPICE_WATCH_EVENT_READ;
+    }
+
     core = red_channel_get_core_interface(rcc->priv->channel);
     core->watch_update_mask(core, rcc->priv->stream->watch, event_mask);
 }
 
+void red_channel_client_block_read(RedChannelClient *rcc)
+{
+    if (rcc->priv->block_read) {
+        return;
+    }
+    rcc->priv->block_read = true;
+    red_channel_client_watch_update_mask(rcc, SPICE_WATCH_EVENT_WRITE);
+}
+
+void red_channel_client_unblock_read(RedChannelClient *rcc)
+{
+    if (!rcc->priv->block_read) {
+        return;
+    }
+    rcc->priv->block_read = false;
+    red_channel_client_watch_update_mask(rcc, SPICE_WATCH_EVENT_READ|SPICE_WATCH_EVENT_WRITE);
+}
+
 static void red_channel_client_seamless_migration_done(RedChannelClient *rcc)
 {
     rcc->priv->wait_migrate_data = FALSE;
@@ -1213,6 +1236,11 @@ static void red_channel_client_handle_incoming(RedChannelClient *rcc)
         if (buffer->msg_pos < msg_size) {
             if (!buffer->msg) {
                 buffer->msg = red_channel_client_alloc_msg_buf(rcc, msg_type, msg_size);
+                if (buffer->msg == NULL && rcc->priv->block_read) {
+                    // if we are blocked by flow control just return, message will be read
+                    // when data will be available
+                    return;
+                }
                 if (buffer->msg == NULL) {
                     red_channel_warning(channel, "ERROR: channel refused to allocate buffer.");
                     red_channel_client_disconnect(rcc);
diff --git a/server/red-channel-client.h b/server/red-channel-client.h
index 1ca0ad71..56e006ec 100644
--- a/server/red-channel-client.h
+++ b/server/red-channel-client.h
@@ -137,6 +137,10 @@ gboolean red_channel_client_set_migration_seamless(RedChannelClient *rcc);
 void red_channel_client_set_destroying(RedChannelClient *rcc);
 bool red_channel_client_is_destroying(RedChannelClient *rcc);
 
+/* allow to block or unblock reading */
+void red_channel_client_block_read(RedChannelClient *rcc);
+void red_channel_client_unblock_read(RedChannelClient *rcc);
+
 struct RedChannelClient
 {
     GObject parent;


More information about the Spice-commits mailing list