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