Blame SOURCES/0059-Prevent-DoS-from-guest-trying-to-allocate-too-much-d.patch

e2c81d
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
73b8f2
From: Frediano Ziglio <fziglio@redhat.com>
73b8f2
Date: Tue, 8 Sep 2015 12:28:54 +0100
e2c81d
Subject: [PATCH] Prevent DoS from guest trying to allocate too much data on
e2c81d
 host for chunks
73b8f2
73b8f2
Limit number of chunks to a given amount to avoid guest trying to
73b8f2
allocate too much memory. Using circular or nested chunks lists
73b8f2
guest could try to allocate huge amounts of memory.
73b8f2
Considering the list can be infinite and guest can change data this
73b8f2
also prevents strange security attacks from guest.
73b8f2
73b8f2
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
73b8f2
---
73b8f2
 server/red_parse_qxl.c | 49 +++++++++++++++++++++++++++++++++++++++++--------
73b8f2
 1 file changed, 41 insertions(+), 8 deletions(-)
73b8f2
73b8f2
diff --git a/server/red_parse_qxl.c b/server/red_parse_qxl.c
73b8f2
index fe3ae78..f183248 100644
73b8f2
--- a/server/red_parse_qxl.c
73b8f2
+++ b/server/red_parse_qxl.c
73b8f2
@@ -37,6 +37,13 @@
73b8f2
 
73b8f2
 G_STATIC_ASSERT(MAX_DATA_CHUNK <= G_MAXINT32);
73b8f2
 
73b8f2
+/* Limit number of chunks.
73b8f2
+ * The guest can attempt to make host allocate too much memory
73b8f2
+ * just with a large number of small chunks.
73b8f2
+ * Prevent that the chunk list take more memory than the data itself.
73b8f2
+ */
73b8f2
+#define MAX_CHUNKS (MAX_DATA_CHUNK/1024u)
73b8f2
+
73b8f2
 #if 0
73b8f2
 static void hexdump_qxl(RedMemSlotInfo *slots, int group_id,
73b8f2
                         QXLPHYSICAL addr, uint8_t bytes)
73b8f2
@@ -100,9 +107,11 @@ static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
73b8f2
                                       RedDataChunk *red, QXLDataChunk *qxl)
73b8f2
 {
73b8f2
     RedDataChunk *red_prev;
73b8f2
-    size_t data_size = 0;
73b8f2
+    uint64_t data_size = 0;
73b8f2
+    uint32_t chunk_data_size;
73b8f2
     int error;
73b8f2
     QXLPHYSICAL next_chunk;
73b8f2
+    unsigned num_chunks = 0;
73b8f2
 
73b8f2
     red->data_size = qxl->data_size;
73b8f2
     data_size += red->data_size;
73b8f2
@@ -114,19 +123,43 @@ static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
73b8f2
     }
73b8f2
 
73b8f2
     while ((next_chunk = qxl->next_chunk) != 0) {
73b8f2
+        /* somebody is trying to use too much memory using a lot of chunks.
73b8f2
+         * Or made a circular list of chunks
73b8f2
+         */
73b8f2
+        if (++num_chunks >= MAX_CHUNKS) {
73b8f2
+            spice_warning("data split in too many chunks, avoiding DoS\n");
73b8f2
+            goto error;
73b8f2
+        }
73b8f2
+
73b8f2
+        memslot_id = get_memslot_id(slots, next_chunk);
73b8f2
+        qxl = (QXLDataChunk *)get_virt(slots, next_chunk, sizeof(*qxl),
73b8f2
+                                       group_id, &error);
73b8f2
+        if (error)
73b8f2
+            goto error;
73b8f2
+
73b8f2
+        /* do not waste space for empty chunks.
73b8f2
+         * This could be just a driver issue or an attempt
73b8f2
+         * to allocate too much memory or a circular list.
73b8f2
+         * All above cases are handled by the check for number
73b8f2
+         * of chunks.
73b8f2
+         */
73b8f2
+        chunk_data_size = qxl->data_size;
73b8f2
+        if (chunk_data_size == 0)
73b8f2
+            continue;
73b8f2
+
73b8f2
         red_prev = red;
73b8f2
         red = spice_new0(RedDataChunk, 1);
73b8f2
+        red->data_size = chunk_data_size;
73b8f2
         red->prev_chunk = red_prev;
73b8f2
+        red->data = qxl->data;
73b8f2
         red_prev->next_chunk = red;
73b8f2
 
73b8f2
-        memslot_id = get_memslot_id(slots, next_chunk);
73b8f2
-        qxl = (QXLDataChunk *)get_virt(slots, next_chunk, sizeof(*qxl), group_id,
73b8f2
-                                      &error);
73b8f2
-        if (error)
73b8f2
+        data_size += chunk_data_size;
73b8f2
+        /* this can happen if client is sending nested chunks */
73b8f2
+        if (data_size > MAX_DATA_CHUNK) {
73b8f2
+            spice_warning("too much data inside chunks, avoiding DoS\n");
73b8f2
             goto error;
73b8f2
-        red->data_size = qxl->data_size;
73b8f2
-        data_size += red->data_size;
73b8f2
-        red->data = qxl->data;
73b8f2
+        }
73b8f2
         if (!validate_virt(slots, (intptr_t)red->data, memslot_id, red->data_size, group_id))
73b8f2
             goto error;
73b8f2
     }