summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/red_parse_qxl.c49
1 files changed, 41 insertions, 8 deletions
diff --git a/server/red_parse_qxl.c b/server/red_parse_qxl.c
index f425869..5513e82 100644
--- a/server/red_parse_qxl.c
+++ b/server/red_parse_qxl.c
@@ -37,6 +37,13 @@
G_STATIC_ASSERT(MAX_DATA_CHUNK <= G_MAXINT32);
+/* Limit number of chunks.
+ * The guest can attempt to make host allocate too much memory
+ * just with a large number of small chunks.
+ * Prevent that the chunk list take more memory than the data itself.
+ */
+#define MAX_CHUNKS (MAX_DATA_CHUNK/1024u)
+
#if 0
static void hexdump_qxl(RedMemSlotInfo *slots, int group_id,
QXLPHYSICAL addr, uint8_t bytes)
@@ -100,9 +107,11 @@ static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
RedDataChunk *red, QXLDataChunk *qxl)
{
RedDataChunk *red_prev;
- size_t data_size = 0;
+ uint64_t data_size = 0;
+ uint32_t chunk_data_size;
int error;
QXLPHYSICAL next_chunk;
+ unsigned num_chunks = 0;
red->data_size = qxl->data_size;
data_size += red->data_size;
@@ -114,19 +123,43 @@ static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
}
while ((next_chunk = qxl->next_chunk) != 0) {
+ /* somebody is trying to use too much memory using a lot of chunks.
+ * Or made a circular list of chunks
+ */
+ if (++num_chunks >= MAX_CHUNKS) {
+ spice_warning("data split in too many chunks, avoiding DoS\n");
+ goto error;
+ }
+
+ memslot_id = get_memslot_id(slots, next_chunk);
+ qxl = (QXLDataChunk *)get_virt(slots, next_chunk, sizeof(*qxl),
+ group_id, &error);
+ if (error)
+ goto error;
+
+ /* do not waste space for empty chunks.
+ * This could be just a driver issue or an attempt
+ * to allocate too much memory or a circular list.
+ * All above cases are handled by the check for number
+ * of chunks.
+ */
+ chunk_data_size = qxl->data_size;
+ if (chunk_data_size == 0)
+ continue;
+
red_prev = red;
red = spice_new0(RedDataChunk, 1);
+ red->data_size = chunk_data_size;
red->prev_chunk = red_prev;
+ red->data = qxl->data;
red_prev->next_chunk = red;
- memslot_id = get_memslot_id(slots, next_chunk);
- qxl = (QXLDataChunk *)get_virt(slots, next_chunk, sizeof(*qxl), group_id,
- &error);
- if (error)
+ data_size += chunk_data_size;
+ /* this can happen if client is sending nested chunks */
+ if (data_size > MAX_DATA_CHUNK) {
+ spice_warning("too much data inside chunks, avoiding DoS\n");
goto error;
- red->data_size = qxl->data_size;
- data_size += red->data_size;
- red->data = qxl->data;
+ }
if (!validate_virt(slots, (intptr_t)red->data, memslot_id, red->data_size, group_id))
goto error;
}