Blame SOURCES/kvm-block-always-fill-entire-LUKS-header-space-with-zero.patch

ddf19c
From 67f36d057aa71ca56ebc17ef28a7cb70bac6c6b6 Mon Sep 17 00:00:00 2001
ddf19c
From: "Daniel P. Berrange" <berrange@redhat.com>
ddf19c
Date: Tue, 5 May 2020 16:46:01 +0100
ddf19c
Subject: [PATCH 01/12] block: always fill entire LUKS header space with zeros
ddf19c
MIME-Version: 1.0
ddf19c
Content-Type: text/plain; charset=UTF-8
ddf19c
Content-Transfer-Encoding: 8bit
ddf19c
ddf19c
RH-Author: Daniel P. Berrange <berrange@redhat.com>
ddf19c
Message-id: <20200505164601.1059974-2-berrange@redhat.com>
ddf19c
Patchwork-id: 96277
ddf19c
O-Subject: [RHEL-AV-8.2.1 qemu-kvm PATCH 1/1] block: always fill entire LUKS header space with zeros
ddf19c
Bugzilla: 1775462
ddf19c
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
ddf19c
RH-Acked-by: John Snow <jsnow@redhat.com>
ddf19c
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
ddf19c
ddf19c
When initializing the LUKS header the size with default encryption
ddf19c
parameters will currently be 2068480 bytes. This is rounded up to
ddf19c
a multiple of the cluster size, 2081792, with 64k sectors. If the
ddf19c
end of the header is not the same as the end of the cluster we fill
ddf19c
the extra space with zeros. This was forgetting that not even the
ddf19c
space allocated for the header will be fully initialized, as we
ddf19c
only write key material for the first key slot. The space left
ddf19c
for the other 7 slots is never written to.
ddf19c
ddf19c
An optimization to the ref count checking code:
ddf19c
ddf19c
  commit a5fff8d4b4d928311a5005efa12d0991fe3b66f9 (refs/bisect/bad)
ddf19c
  Author: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
ddf19c
  Date:   Wed Feb 27 16:14:30 2019 +0300
ddf19c
ddf19c
    qcow2-refcount: avoid eating RAM
ddf19c
ddf19c
made the assumption that every cluster which was allocated would
ddf19c
have at least some data written to it. This was violated by way
ddf19c
the LUKS header is only partially written, with much space simply
ddf19c
reserved for future use.
ddf19c
ddf19c
Depending on the cluster size this problem was masked by the
ddf19c
logic which wrote zeros between the end of the LUKS header and
ddf19c
the end of the cluster.
ddf19c
ddf19c
$ qemu-img create --object secret,id=cluster_encrypt0,data=123456 \
ddf19c
   -f qcow2 -o cluster_size=2k,encrypt.iter-time=1,\
ddf19c
               encrypt.format=luks,encrypt.key-secret=cluster_encrypt0 \
ddf19c
               cluster_size_check.qcow2 100M
ddf19c
  Formatting 'cluster_size_check.qcow2', fmt=qcow2 size=104857600
ddf19c
    encrypt.format=luks encrypt.key-secret=cluster_encrypt0
ddf19c
    encrypt.iter-time=1 cluster_size=2048 lazy_refcounts=off refcount_bits=16
ddf19c
ddf19c
$ qemu-img check --object secret,id=cluster_encrypt0,data=redhat \
ddf19c
    'json:{"driver": "qcow2", "encrypt.format": "luks", \
ddf19c
           "encrypt.key-secret": "cluster_encrypt0", \
ddf19c
           "file.driver": "file", "file.filename": "cluster_size_check.qcow2"}'
ddf19c
ERROR: counting reference for region exceeding the end of the file by one cluster or more: offset 0x2000 size 0x1f9000
ddf19c
Leaked cluster 4 refcount=1 reference=0
ddf19c
...snip...
ddf19c
Leaked cluster 130 refcount=1 reference=0
ddf19c
ddf19c
1 errors were found on the image.
ddf19c
Data may be corrupted, or further writes to the image may corrupt it.
ddf19c
ddf19c
127 leaked clusters were found on the image.
ddf19c
This means waste of disk space, but no harm to data.
ddf19c
Image end offset: 268288
ddf19c
ddf19c
The problem only exists when the disk image is entirely empty. Writing
ddf19c
data to the disk image payload will solve the problem by causing the
ddf19c
end of the file to be extended further.
ddf19c
ddf19c
The change fixes it by ensuring that the entire allocated LUKS header
ddf19c
region is fully initialized with zeros. The qemu-img check will still
ddf19c
fail for any pre-existing disk images created prior to this change,
ddf19c
unless at least 1 byte of the payload is written to.
ddf19c
ddf19c
Fully writing zeros to the entire LUKS header is a good idea regardless
ddf19c
as it ensures that space has been allocated on the host filesystem (or
ddf19c
whatever block storage backend is used).
ddf19c
ddf19c
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
ddf19c
Message-Id: <20200207135520.2669430-1-berrange@redhat.com>
ddf19c
Reviewed-by: Eric Blake <eblake@redhat.com>
ddf19c
Signed-off-by: Max Reitz <mreitz@redhat.com>
ddf19c
(cherry picked from commit 087ab8e775f48766068e65de1bc99d03b40d1670)
ddf19c
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
ddf19c
ddf19c
Conflicts:
ddf19c
	tests/qemu-iotests/group: no test 283 in downstream
ddf19c
ddf19c
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
ddf19c
---
ddf19c
 block/qcow2.c              | 11 ++++--
ddf19c
 tests/qemu-iotests/284     | 97 ++++++++++++++++++++++++++++++++++++++++++++++
ddf19c
 tests/qemu-iotests/284.out | 62 +++++++++++++++++++++++++++++
ddf19c
 tests/qemu-iotests/group   |  1 +
ddf19c
 4 files changed, 167 insertions(+), 4 deletions(-)
ddf19c
 create mode 100755 tests/qemu-iotests/284
ddf19c
 create mode 100644 tests/qemu-iotests/284.out
ddf19c
ddf19c
diff --git a/block/qcow2.c b/block/qcow2.c
ddf19c
index 71067c6..af0ad4a 100644
ddf19c
--- a/block/qcow2.c
ddf19c
+++ b/block/qcow2.c
ddf19c
@@ -135,13 +135,16 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,
ddf19c
     s->crypto_header.length = headerlen;
ddf19c
     s->crypto_header.offset = ret;
ddf19c
 
ddf19c
-    /* Zero fill remaining space in cluster so it has predictable
ddf19c
-     * content in case of future spec changes */
ddf19c
+    /*
ddf19c
+     * Zero fill all space in cluster so it has predictable
ddf19c
+     * content, as we may not initialize some regions of the
ddf19c
+     * header (eg only 1 out of 8 key slots will be initialized)
ddf19c
+     */
ddf19c
     clusterlen = size_to_clusters(s, headerlen) * s->cluster_size;
ddf19c
     assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0);
ddf19c
     ret = bdrv_pwrite_zeroes(bs->file,
ddf19c
-                             ret + headerlen,
ddf19c
-                             clusterlen - headerlen, 0);
ddf19c
+                             ret,
ddf19c
+                             clusterlen, 0);
ddf19c
     if (ret < 0) {
ddf19c
         error_setg_errno(errp, -ret, "Could not zero fill encryption header");
ddf19c
         return -1;
ddf19c
diff --git a/tests/qemu-iotests/284 b/tests/qemu-iotests/284
ddf19c
new file mode 100755
ddf19c
index 0000000..071e89b
ddf19c
--- /dev/null
ddf19c
+++ b/tests/qemu-iotests/284
ddf19c
@@ -0,0 +1,97 @@
ddf19c
+#!/usr/bin/env bash
ddf19c
+#
ddf19c
+# Test ref count checks on encrypted images
ddf19c
+#
ddf19c
+# Copyright (C) 2019 Red Hat, Inc.
ddf19c
+#
ddf19c
+# This program is free software; you can redistribute it and/or modify
ddf19c
+# it under the terms of the GNU General Public License as published by
ddf19c
+# the Free Software Foundation; either version 2 of the License, or
ddf19c
+# (at your option) any later version.
ddf19c
+#
ddf19c
+# This program is distributed in the hope that it will be useful,
ddf19c
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
ddf19c
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ddf19c
+# GNU General Public License for more details.
ddf19c
+#
ddf19c
+# You should have received a copy of the GNU General Public License
ddf19c
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
ddf19c
+#
ddf19c
+
ddf19c
+# creator
ddf19c
+owner=berrange@redhat.com
ddf19c
+
ddf19c
+seq=`basename $0`
ddf19c
+echo "QA output created by $seq"
ddf19c
+
ddf19c
+status=1        # failure is the default!
ddf19c
+
ddf19c
+_cleanup()
ddf19c
+{
ddf19c
+        _cleanup_test_img
ddf19c
+}
ddf19c
+trap "_cleanup; exit \$status" 0 1 2 3 15
ddf19c
+
ddf19c
+# get standard environment, filters and checks
ddf19c
+. ./common.rc
ddf19c
+. ./common.filter
ddf19c
+
ddf19c
+_supported_fmt qcow2
ddf19c
+_supported_proto generic
ddf19c
+_supported_os Linux
ddf19c
+
ddf19c
+
ddf19c
+size=1M
ddf19c
+
ddf19c
+SECRET="secret,id=sec0,data=astrochicken"
ddf19c
+
ddf19c
+IMGSPEC="driver=$IMGFMT,file.filename=$TEST_IMG,encrypt.key-secret=sec0"
ddf19c
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
ddf19c
+
ddf19c
+_run_test()
ddf19c
+{
ddf19c
+        IMGOPTSSYNTAX=true
ddf19c
+        OLD_TEST_IMG="$TEST_IMG"
ddf19c
+        TEST_IMG="driver=$IMGFMT,file.filename=$TEST_IMG,encrypt.key-secret=sec0"
ddf19c
+        QEMU_IMG_EXTRA_ARGS="--image-opts --object $SECRET"
ddf19c
+
ddf19c
+        echo
ddf19c
+        echo "== cluster size $csize"
ddf19c
+        echo "== checking image refcounts =="
ddf19c
+        _check_test_img
ddf19c
+
ddf19c
+        echo
ddf19c
+        echo "== writing some data =="
ddf19c
+        $QEMU_IO -c "write -P 0x9 0 1"  $QEMU_IMG_EXTRA_ARGS $TEST_IMG | _filter_qemu_io | _filter_testdir
ddf19c
+        echo
ddf19c
+        echo "== rechecking image refcounts =="
ddf19c
+        _check_test_img
ddf19c
+
ddf19c
+        echo
ddf19c
+        echo "== writing some more data =="
ddf19c
+        $QEMU_IO -c "write -P 0x9 $csize 1" $QEMU_IMG_EXTRA_ARGS $TEST_IMG | _filter_qemu_io | _filter_testdir
ddf19c
+        echo
ddf19c
+        echo "== rechecking image refcounts =="
ddf19c
+        _check_test_img
ddf19c
+
ddf19c
+        TEST_IMG="$OLD_TEST_IMG"
ddf19c
+        QEMU_IMG_EXTRA_ARGS=
ddf19c
+        IMGOPTSSYNTAX=
ddf19c
+}
ddf19c
+
ddf19c
+
ddf19c
+echo
ddf19c
+echo "testing LUKS qcow2 encryption"
ddf19c
+echo
ddf19c
+
ddf19c
+for csize in 512 2048 32768
ddf19c
+do
ddf19c
+  _make_test_img --object $SECRET -o "encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10,cluster_size=$csize" $size
ddf19c
+  _run_test
ddf19c
+  _cleanup_test_img
ddf19c
+done
ddf19c
+
ddf19c
+# success, all done
ddf19c
+echo "*** done"
ddf19c
+rm -f $seq.full
ddf19c
+status=0
ddf19c
diff --git a/tests/qemu-iotests/284.out b/tests/qemu-iotests/284.out
ddf19c
new file mode 100644
ddf19c
index 0000000..48216f5
ddf19c
--- /dev/null
ddf19c
+++ b/tests/qemu-iotests/284.out
ddf19c
@@ -0,0 +1,62 @@
ddf19c
+QA output created by 284
ddf19c
+
ddf19c
+testing LUKS qcow2 encryption
ddf19c
+
ddf19c
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
ddf19c
+
ddf19c
+== cluster size 512
ddf19c
+== checking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+
ddf19c
+== writing some data ==
ddf19c
+wrote 1/1 bytes at offset 0
ddf19c
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
ddf19c
+
ddf19c
+== rechecking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+
ddf19c
+== writing some more data ==
ddf19c
+wrote 1/1 bytes at offset 512
ddf19c
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
ddf19c
+
ddf19c
+== rechecking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
ddf19c
+
ddf19c
+== cluster size 2048
ddf19c
+== checking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+
ddf19c
+== writing some data ==
ddf19c
+wrote 1/1 bytes at offset 0
ddf19c
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
ddf19c
+
ddf19c
+== rechecking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+
ddf19c
+== writing some more data ==
ddf19c
+wrote 1/1 bytes at offset 2048
ddf19c
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
ddf19c
+
ddf19c
+== rechecking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
ddf19c
+
ddf19c
+== cluster size 32768
ddf19c
+== checking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+
ddf19c
+== writing some data ==
ddf19c
+wrote 1/1 bytes at offset 0
ddf19c
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
ddf19c
+
ddf19c
+== rechecking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+
ddf19c
+== writing some more data ==
ddf19c
+wrote 1/1 bytes at offset 32768
ddf19c
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
ddf19c
+
ddf19c
+== rechecking image refcounts ==
ddf19c
+No errors were found on the image.
ddf19c
+*** done
ddf19c
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
ddf19c
index e47cbfc..9c565cf 100644
ddf19c
--- a/tests/qemu-iotests/group
ddf19c
+++ b/tests/qemu-iotests/group
ddf19c
@@ -289,3 +289,4 @@
ddf19c
 277 rw quick
ddf19c
 280 rw migration quick
ddf19c
 281 rw quick
ddf19c
+284 rw
ddf19c
-- 
ddf19c
1.8.3.1
ddf19c