[Spice-devel] [PATCH spice-server 3/3] spicevmc: Avoids DoS if client is not able to get data faster enough

Frediano Ziglio fziglio at redhat.com
Sat Jun 1 12:14:13 UTC 2019


This fix half (one direction) of
https://gitlab.freedesktop.org/spice/spice/issues/29.
Specifically if you have attempt to transfer a file to the client
using WebDAV.
Previously the queue to the client was unbound. If client was not
getting data fast enough the server started queuing data.
To easily test this you can use a tool to limit bandwidth and
transfer a big file (I used a "dd if=/dev/zero bs=1M of=test") from
the guest.

Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
 server/spicevmc.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/server/spicevmc.c b/server/spicevmc.c
index 8c41573ae..906c2441e 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -42,6 +42,10 @@
 #define BUF_SIZE (64 * 1024 + 32)
 #define COMPRESS_THRESHOLD 1000
 
+// limit of the queued data, at this limit we stop reading from device to
+// avoid DoS
+#define QUEUED_DATA_LIMIT (1024*1024)
+
 SPICE_DECLARE_TYPE(RedCharDeviceSpiceVmc, red_char_device_spicevmc, CHAR_DEVICE_SPICEVMC);
 #define RED_TYPE_CHAR_DEVICE_SPICEVMC red_char_device_spicevmc_get_type()
 
@@ -83,6 +87,7 @@ struct RedCharDeviceSpiceVmcClass
 static RedCharDevice *red_char_device_spicevmc_new(SpiceCharDeviceInstance *sin,
                                                    RedsState *reds,
                                                    RedVmcChannel *channel);
+static void spicevmc_red_channel_queue_data(RedVmcChannel *channel, RedVmcPipeItem *item);
 
 G_DEFINE_TYPE(RedCharDeviceSpiceVmc, red_char_device_spicevmc, RED_TYPE_CHAR_DEVICE)
 
@@ -96,6 +101,7 @@ struct RedVmcChannel
     RedVmcPipeItem *pipe_item;
     RedCharDeviceWriteBuffer *recv_from_client_buf;
     uint8_t port_opened;
+    uint32_t queued_data;
     RedStatCounter in_data;
     RedStatCounter in_compressed;
     RedStatCounter in_decompressed;
@@ -337,7 +343,7 @@ static RedPipeItem *spicevmc_chardev_read_msg_from_dev(RedCharDevice *self,
 
     sif = spice_char_device_get_interface(sin);
 
-    if (!channel->rcc) {
+    if (!channel->rcc || channel->queued_data >= QUEUED_DATA_LIMIT) {
         return NULL;
     }
 
@@ -360,14 +366,14 @@ static RedPipeItem *spicevmc_chardev_read_msg_from_dev(RedCharDevice *self,
 
         msg_item_compressed = try_compress_lz4(channel, n, msg_item);
         if (msg_item_compressed != NULL) {
-            red_channel_client_pipe_add_push(channel->rcc, &msg_item_compressed->base);
+            spicevmc_red_channel_queue_data(channel, msg_item_compressed);
             return NULL;
         }
 #endif
         stat_inc_counter(channel->out_data, n);
         msg_item->uncompressed_data_size = n;
         msg_item->buf_used = n;
-        red_channel_client_pipe_add_push(channel->rcc, &msg_item->base);
+        spicevmc_red_channel_queue_data(channel, msg_item);
         return NULL;
     }
     channel->pipe_item = msg_item;
@@ -609,11 +615,19 @@ static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc,
     }
 }
 
+static void
+spicevmc_red_channel_queue_data(RedVmcChannel *channel, RedVmcPipeItem *item)
+{
+    channel->queued_data += item->buf_used;
+    red_channel_client_pipe_add_push(channel->rcc, &item->base);
+}
+
 static void spicevmc_red_channel_send_data(RedChannelClient *rcc,
                                            SpiceMarshaller *m,
                                            RedPipeItem *item)
 {
     RedVmcPipeItem *i = SPICE_UPCAST(RedVmcPipeItem, item);
+    RedVmcChannel *channel = RED_VMC_CHANNEL(red_channel_client_get_channel(rcc));
 
     /* for compatibility send using not compressed data message */
     if (i->type == SPICE_DATA_COMPRESSION_TYPE_NONE) {
@@ -630,6 +644,14 @@ static void spicevmc_red_channel_send_data(RedChannelClient *rcc,
     red_pipe_item_ref(item);
     spice_marshaller_add_by_ref_full(m, i->buf, i->buf_used,
                                      marshaller_unref_pipe_item, item);
+
+    // account for sent data and wake up device if was blocked
+    uint32_t old_queued_data = channel->queued_data;
+    channel->queued_data -= i->buf_used;
+    if (channel->chardev &&
+        old_queued_data >= QUEUED_DATA_LIMIT && channel->queued_data < QUEUED_DATA_LIMIT) {
+        red_char_device_wakeup(channel->chardev);
+    }
 }
 
 static void spicevmc_red_channel_send_migrate_data(RedChannelClient *rcc,
@@ -768,6 +790,7 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client,
         return;
     }
     vmc_channel->rcc = rcc;
+    vmc_channel->queued_data = 0;
     red_channel_client_ack_zero_messages_window(rcc);
 
     if (strcmp(sin->subtype, "port") == 0) {
-- 
2.20.1



More information about the Spice-devel mailing list