qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v2 2/5] qcow2: Metadata overlap checks


From: Max Reitz
Subject: Re: [Qemu-devel] [PATCH v2 2/5] qcow2: Metadata overlap checks
Date: Thu, 29 Aug 2013 10:57:07 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130805 Thunderbird/17.0.8

Am 29.08.2013 10:51, schrieb Kevin Wolf:
Am 28.08.2013 um 16:55 hat Max Reitz geschrieben:
Two new functions are added; the first one checks a given range in the
image file for overlaps with metadata (main header, L1 tables, L2
tables, refcount table and blocks).

The second one should be used immediately before writing to the image
file as it calls the first function and, upon collision, marks the
image as corrupt and makes the BDS unusable, thereby preventing
further access.

Both functions take a bitmask argument specifying the structures which
should be checked for overlaps, making it possible to also check
metadata writes against colliding with other structures.

Signed-off-by: Max Reitz <address@hidden>
---
  block/qcow2-refcount.c    | 148 ++++++++++++++++++++++++++++++++++++++++++++++
  block/qcow2.h             |  28 +++++++++
  include/monitor/monitor.h |   1 +
  monitor.c                 |   1 +
  4 files changed, 178 insertions(+)

diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 1244693..d06a9df 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -25,6 +25,7 @@
  #include "qemu-common.h"
  #include "block/block_int.h"
  #include "block/qcow2.h"
+#include "qemu/range.h"
static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size);
  static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
@@ -1372,3 +1373,150 @@ fail:
      return ret;
  }
+#define overlaps_with(ofs, sz) \
+    ranges_overlap(offset, size, \
+        (ofs) & ~(cluster_mask), \
+        ((sz) + ((ofs) & (cluster_mask)) + (cluster_mask)) & ~(cluster_mask))
It's not actually necessary to have both ranges rounded to cluster
boundaries, one of them is enough to detect all overlaps. So you can
simplify either this macro or the code below.
Right, the macro it is, then.

+/*
+ * Checks if the given offset into the image file is actually free to use by
+ * looking for overlaps with important metadata sections (L1/L2 tables etc.),
+ * i.e. a sanity check without relying on the refcount tables.
+ *
+ * The chk parameter specifies exactly what checks to perform (being a bitmask
+ * of QCow2MetadataOverlap values).
+ *
+ * Returns:
+ * - 0 if writing to this offset will not affect the mentioned metadata
+ * - a positive QCow2MetadataOverlap value indicating one overlapping section
+ * - a negative value (-errno) indicating an error while performing a check,
+ *   e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2
+ */
+int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
+                                 int64_t size)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t cluster_mask = s->cluster_size - 1;
+    int i, j;
+
+    if (!size) {
+        return 0;
+    }
+
+    if (chk & QCOW2_OL_MAIN_HEADER) {
+        if (offset < s->cluster_size) {
+            return QCOW2_OL_MAIN_HEADER;
+        }
+    }
+
+    size = (size + (offset & cluster_mask) + cluster_mask) & ~cluster_mask;
+    offset &= ~cluster_mask;
Attempt for improved readability:

size = align_offset(offset_into_cluster(s, offset) + size, s->cluster_size);
offset = start_of_cluster(s, offset);

What do you think?
I like start_of_cluster, but I don't know whether I really like align_offset (I just don't like its name) - but it's the right function, so I'll go with it.

+    } else if (ret > 0) {
+        fprintf(stderr, "qcow2: Preventing invalid write on metadata; "
+                "image marked as corrupt.\n");
+        bdrv_emit_qmp_error_event(bs, QEVENT_BLOCK_IMAGE_CORRUPTED,
+                BDRV_ACTION_REPORT, false);
The dummy BDRV_ACTION_REPORT shows that you're abusing an interface for
something it was never meant for. The additional information of
QEVENT_BLOCK_IO_ERROR doesn't really make sense here, but we have other
information to report.

Let's directly create a JSON message and call monitor_protocol_event()
here. We can tell what kind of metadata would have been overwritten and
at which offset/size this happened, like:

     static const char *metadata_ol_names[] = {
         [QCOW2_OL_MAIN_HEADER]  = "qcow2 header",
         [QCOW2_OL_ACTIVE_L1]    = "active L1 table",
         ...
     };

     assert(ret < ARRAY_SIZE(metadata_ol_names));
     message = g_strdup_printf("Prevented %s overwrite", 
metadata_ol_names[ret]);
     data = qobject_from_jsonf("{ 'device': %s, 'msg': %s, "
                               "'offset': %" PRId64 ", 'size': %" PRId64 " }",
                               bdrv->device_name, message, offset, size);
     monitor_protocol_event(QEVENT_BLOCK_IMAGE_CORRUPTED, data);
Okay, thanks.


Max



reply via email to

[Prev in Thread] Current Thread [Next in Thread]