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

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