0a122b
From 427c0839e73f5bb1b3b0fa33afd4a9e5ea3eb34b Mon Sep 17 00:00:00 2001
0a122b
From: Kevin Wolf <kwolf@redhat.com>
0a122b
Date: Tue, 25 Mar 2014 14:23:53 +0100
0a122b
Subject: [PATCH 46/49] qcow2: Limit snapshot table size
0a122b
0a122b
RH-Author: Kevin Wolf <kwolf@redhat.com>
0a122b
Message-id: <1395753835-7591-47-git-send-email-kwolf@redhat.com>
0a122b
Patchwork-id: n/a
0a122b
O-Subject: [virt-devel] [EMBARGOED RHEL-7.0 qemu-kvm PATCH 46/48] qcow2: Limit snapshot table size
0a122b
Bugzilla: 1066691
0a122b
RH-Acked-by: Jeff Cody <jcody@redhat.com>
0a122b
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
0a122b
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
0a122b
0a122b
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1066691
0a122b
Upstream status: Series embargoed
0a122b
0a122b
Even with a limit of 64k snapshots, each snapshot could have a filename
0a122b
and an ID with up to 64k, which would still lead to pretty large
0a122b
allocations, which could potentially lead to qemu aborting. Limit the
0a122b
total size of the snapshot table to an average of 1k per entry when
0a122b
the limit of 64k snapshots is fully used. This should be plenty for any
0a122b
reasonable user.
0a122b
0a122b
This also fixes potential integer overflows of s->snapshot_size.
0a122b
0a122b
Suggested-by: Max Reitz <mreitz@redhat.com>
0a122b
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
0a122b
---
0a122b
 block/qcow2-snapshot.c |   13 +++++++++++++
0a122b
 block/qcow2.h          |    4 ++++
0a122b
 2 files changed, 17 insertions(+), 0 deletions(-)
0a122b
0a122b
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
0a122b
index 626a8d4..84481be 100644
0a122b
--- a/block/qcow2-snapshot.c
0a122b
+++ b/block/qcow2-snapshot.c
0a122b
@@ -116,8 +116,14 @@ int qcow2_read_snapshots(BlockDriverState *bs)
0a122b
         }
0a122b
         offset += name_size;
0a122b
         sn->name[name_size] = '\0';
0a122b
+
0a122b
+        if (offset - s->snapshots_offset > QCOW_MAX_SNAPSHOTS_SIZE) {
0a122b
+            ret = -EFBIG;
0a122b
+            goto fail;
0a122b
+        }
0a122b
     }
0a122b
 
0a122b
+    assert(offset - s->snapshots_offset <= INT_MAX);
0a122b
     s->snapshots_size = offset - s->snapshots_offset;
0a122b
     return 0;
0a122b
 
0a122b
@@ -150,7 +156,14 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
0a122b
         offset += sizeof(extra);
0a122b
         offset += strlen(sn->id_str);
0a122b
         offset += strlen(sn->name);
0a122b
+
0a122b
+        if (offset > QCOW_MAX_SNAPSHOTS_SIZE) {
0a122b
+            ret = -EFBIG;
0a122b
+            goto fail;
0a122b
+        }
0a122b
     }
0a122b
+
0a122b
+    assert(offset <= INT_MAX);
0a122b
     snapshots_size = offset;
0a122b
 
0a122b
     /* Allocate space for the new snapshot list */
0a122b
diff --git a/block/qcow2.h b/block/qcow2.h
0a122b
index 87e256a..1cc1ef0 100644
0a122b
--- a/block/qcow2.h
0a122b
+++ b/block/qcow2.h
0a122b
@@ -48,6 +48,10 @@
0a122b
  * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
0a122b
 #define QCOW_MAX_L1_SIZE 0x2000000
0a122b
 
0a122b
+/* Allow for an average of 1k per snapshot table entry, should be plenty of
0a122b
+ * space for snapshot names and IDs */
0a122b
+#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS)
0a122b
+
0a122b
 /* indicate that the refcount of the referenced cluster is exactly one. */
0a122b
 #define QCOW_OFLAG_COPIED     (1LL << 63)
0a122b
 /* indicate that the cluster is compressed (they never have the copied flag) */
0a122b
-- 
0a122b
1.7.1
0a122b