fbe740
From d9369eae049c780ebd130a13eedfaaf3586242eb Mon Sep 17 00:00:00 2001
fbe740
Message-Id: <d9369eae049c780ebd130a13eedfaaf3586242eb@dist-git>
fbe740
From: Michal Privoznik <mprivozn@redhat.com>
fbe740
Date: Mon, 9 Mar 2020 14:58:57 +0100
fbe740
Subject: [PATCH] qemu: Tell secdrivers which images are top parent
fbe740
fbe740
When preparing images for block jobs we modify their seclabels so
fbe740
that QEMU can open them. However, as mentioned in the previous
fbe740
commit, secdrivers base some it their decisions whether the image
fbe740
they are working on is top of of the backing chain. Fortunately,
fbe740
in places where we call secdrivers we know this and the
fbe740
information can be passed to secdrivers.
fbe740
fbe740
The problem is the following: after the first blockcommit from
fbe740
the base to one of the parents the XATTRs on the base image are
fbe740
not cleared and therefore the second attempt to do another
fbe740
blockcommit fails. This is caused by blockcommit code calling
fbe740
qemuSecuritySetImageLabel() over the base image, possibly
fbe740
multiple times (to ensure RW/RO access). A naive fix would be to
fbe740
call the restore function. But this is not possible, because that
fbe740
would deny QEMU the access to the base image.  Fortunately, we
fbe740
can use the fact that seclabels are remembered only for the top
fbe740
of the backing chain and not for the rest of the backing chain.
fbe740
And thanks to the previous commit we can tell secdrivers which
fbe740
images are top of the backing chain.
fbe740
fbe740
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1803551
fbe740
fbe740
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
fbe740
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
fbe740
(cherry picked from commit 13eb6c14682004d0dc692554475125db0f161da7)
fbe740
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
fbe740
Message-Id: <a8d9171d9f88b325480c17969e074d62d3eeba3b.1583760062.git.mprivozn@redhat.com>
fbe740
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
fbe740
---
fbe740
 src/qemu/qemu_backup.c     |  4 ++--
fbe740
 src/qemu/qemu_blockjob.c   |  6 ++++--
fbe740
 src/qemu/qemu_checkpoint.c |  6 ++++--
fbe740
 src/qemu/qemu_domain.c     | 24 ++++++++++++++++++++----
fbe740
 src/qemu/qemu_domain.h     |  3 ++-
fbe740
 src/qemu/qemu_driver.c     | 23 +++++++++++++++++------
fbe740
 src/qemu/qemu_process.c    |  2 +-
fbe740
 src/qemu/qemu_security.c   |  6 +++++-
fbe740
 src/qemu/qemu_security.h   |  3 ++-
fbe740
 9 files changed, 57 insertions(+), 20 deletions(-)
fbe740
fbe740
diff --git a/src/qemu/qemu_backup.c b/src/qemu/qemu_backup.c
fbe740
index 2cc6ff7a42..8b66ee8d1f 100644
fbe740
--- a/src/qemu/qemu_backup.c
fbe740
+++ b/src/qemu/qemu_backup.c
fbe740
@@ -469,8 +469,8 @@ qemuBackupDiskPrepareOneStorage(virDomainObjPtr vm,
fbe740
         dd->created = true;
fbe740
     }
fbe740
 
fbe740
-    if (qemuDomainStorageSourceAccessAllow(priv->driver, vm, dd->store, false,
fbe740
-                                           true) < 0)
fbe740
+    if (qemuDomainStorageSourceAccessAllow(priv->driver, vm, dd->store,
fbe740
+                                           false, true, true) < 0)
fbe740
         return -1;
fbe740
 
fbe740
     dd->labelled = true;
fbe740
diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c
fbe740
index 71df0d1ab2..e894e1634d 100644
fbe740
--- a/src/qemu/qemu_blockjob.c
fbe740
+++ b/src/qemu/qemu_blockjob.c
fbe740
@@ -1105,9 +1105,11 @@ qemuBlockJobProcessEventCompletedCommit(virQEMUDriverPtr driver,
fbe740
         return;
fbe740
 
fbe740
     /* revert access to images */
fbe740
-    qemuDomainStorageSourceAccessAllow(driver, vm, job->data.commit.base, true, false);
fbe740
+    qemuDomainStorageSourceAccessAllow(driver, vm, job->data.commit.base,
fbe740
+                                       true, false, false);
fbe740
     if (job->data.commit.topparent != job->disk->src)
fbe740
-        qemuDomainStorageSourceAccessAllow(driver, vm, job->data.commit.topparent, true, false);
fbe740
+        qemuDomainStorageSourceAccessAllow(driver, vm, job->data.commit.topparent,
fbe740
+                                           true, false, true);
fbe740
 
fbe740
     baseparent->backingStore = NULL;
fbe740
     job->data.commit.topparent->backingStore = job->data.commit.base;
fbe740
diff --git a/src/qemu/qemu_checkpoint.c b/src/qemu/qemu_checkpoint.c
fbe740
index c06bfe6a21..fe54af74ec 100644
fbe740
--- a/src/qemu/qemu_checkpoint.c
fbe740
+++ b/src/qemu/qemu_checkpoint.c
fbe740
@@ -298,7 +298,8 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm,
fbe740
     for (next = reopenimages; next; next = next->next) {
fbe740
         virStorageSourcePtr src = next->data;
fbe740
 
fbe740
-        if (qemuDomainStorageSourceAccessAllow(driver, vm, src, false, false) < 0)
fbe740
+        if (qemuDomainStorageSourceAccessAllow(driver, vm, src,
fbe740
+                                               false, false, false) < 0)
fbe740
             goto relabel;
fbe740
 
fbe740
         relabelimages = g_slist_prepend(relabelimages, src);
fbe740
@@ -313,7 +314,8 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm,
fbe740
     for (next = relabelimages; next; next = next->next) {
fbe740
         virStorageSourcePtr src = next->data;
fbe740
 
fbe740
-        ignore_value(qemuDomainStorageSourceAccessAllow(driver, vm, src, true, false));
fbe740
+        ignore_value(qemuDomainStorageSourceAccessAllow(driver, vm, src,
fbe740
+                                                        true, false, false));
fbe740
     }
fbe740
 
fbe740
     return rc;
fbe740
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
fbe740
index 3cbe7ef6e1..14bab896bc 100644
fbe740
--- a/src/qemu/qemu_domain.c
fbe740
+++ b/src/qemu/qemu_domain.c
fbe740
@@ -11770,6 +11770,8 @@ typedef enum {
fbe740
     QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_SKIP_REVOKE = 1 << 4,
fbe740
     /* VM already has access to the source and we are just modifying it */
fbe740
     QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_MODIFY_ACCESS = 1 << 5,
fbe740
+    /* whether the image is the top image of the backing chain (e.g. disk source) */
fbe740
+    QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN_TOP = 1 << 6,
fbe740
 } qemuDomainStorageSourceAccessFlags;
fbe740
 
fbe740
 
fbe740
@@ -11847,6 +11849,7 @@ qemuDomainStorageSourceAccessModify(virQEMUDriverPtr driver,
fbe740
     bool force_ro = flags & QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_FORCE_READ_ONLY;
fbe740
     bool force_rw = flags & QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_FORCE_READ_WRITE;
fbe740
     bool revoke = flags & QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_REVOKE;
fbe740
+    bool chain_top = flags & QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN_TOP;
fbe740
     int rc;
fbe740
     bool was_readonly = src->readonly;
fbe740
     bool revoke_cgroup = false;
fbe740
@@ -11893,7 +11896,7 @@ qemuDomainStorageSourceAccessModify(virQEMUDriverPtr driver,
fbe740
         revoke_namespace = true;
fbe740
     }
fbe740
 
fbe740
-    if (qemuSecuritySetImageLabel(driver, vm, src, chain) < 0)
fbe740
+    if (qemuSecuritySetImageLabel(driver, vm, src, chain, chain_top) < 0)
fbe740
         goto revoke;
fbe740
 
fbe740
     revoke_label = true;
fbe740
@@ -11956,7 +11959,8 @@ qemuDomainStorageSourceChainAccessAllow(virQEMUDriverPtr driver,
fbe740
                                         virDomainObjPtr vm,
fbe740
                                         virStorageSourcePtr src)
fbe740
 {
fbe740
-    qemuDomainStorageSourceAccessFlags flags = QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN;
fbe740
+    qemuDomainStorageSourceAccessFlags flags = QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN |
fbe740
+                                               QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN_TOP;
fbe740
 
fbe740
     return qemuDomainStorageSourceAccessModify(driver, vm, src, flags);
fbe740
 }
fbe740
@@ -11968,7 +11972,8 @@ qemuDomainStorageSourceChainAccessRevoke(virQEMUDriverPtr driver,
fbe740
                                          virStorageSourcePtr src)
fbe740
 {
fbe740
     qemuDomainStorageSourceAccessFlags flags = QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_REVOKE |
fbe740
-                                               QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN;
fbe740
+                                               QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN |
fbe740
+                                               QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN_TOP;
fbe740
 
fbe740
     return qemuDomainStorageSourceAccessModify(driver, vm, src, flags);
fbe740
 }
fbe740
@@ -11998,6 +12003,7 @@ qemuDomainStorageSourceAccessRevoke(virQEMUDriverPtr driver,
fbe740
  * @elem: source structure to set access for
fbe740
  * @readonly: setup read-only access if true
fbe740
  * @newSource: @elem describes a storage source which @vm can't access yet
fbe740
+ * @chainTop: @elem is top parent of backing chain
fbe740
  *
fbe740
  * Allow a VM access to a single element of a disk backing chain; this helper
fbe740
  * ensures that the lock manager, cgroup device controller, and security manager
fbe740
@@ -12005,13 +12011,20 @@ qemuDomainStorageSourceAccessRevoke(virQEMUDriverPtr driver,
fbe740
  *
fbe740
  * When modifying permissions of @elem which @vm can already access (is in the
fbe740
  * backing chain) @newSource needs to be set to false.
fbe740
+ *
fbe740
+ * The @chainTop flag must be set if the @elem image is the topmost image of a
fbe740
+ * given backing chain or meant to become the topmost image (for e.g.
fbe740
+ * snapshots, or blockcopy or even in the end for active layer block commit,
fbe740
+ * where we discard the top of the backing chain so one of the intermediates
fbe740
+ * (the base) becomes the top of the chain).
fbe740
  */
fbe740
 int
fbe740
 qemuDomainStorageSourceAccessAllow(virQEMUDriverPtr driver,
fbe740
                                    virDomainObjPtr vm,
fbe740
                                    virStorageSourcePtr elem,
fbe740
                                    bool readonly,
fbe740
-                                   bool newSource)
fbe740
+                                   bool newSource,
fbe740
+                                   bool chainTop)
fbe740
 {
fbe740
     qemuDomainStorageSourceAccessFlags flags = QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_SKIP_REVOKE;
fbe740
 
fbe740
@@ -12023,6 +12036,9 @@ qemuDomainStorageSourceAccessAllow(virQEMUDriverPtr driver,
fbe740
     if (!newSource)
fbe740
         flags |= QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_MODIFY_ACCESS;
fbe740
 
fbe740
+    if (chainTop)
fbe740
+        flags |= QEMU_DOMAIN_STORAGE_SOURCE_ACCESS_CHAIN_TOP;
fbe740
+
fbe740
     return qemuDomainStorageSourceAccessModify(driver, vm, elem, flags);
fbe740
 }
fbe740
 
fbe740
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
fbe740
index 83150e4e6d..9d143b575d 100644
fbe740
--- a/src/qemu/qemu_domain.h
fbe740
+++ b/src/qemu/qemu_domain.h
fbe740
@@ -895,7 +895,8 @@ int qemuDomainStorageSourceAccessAllow(virQEMUDriverPtr driver,
fbe740
                                        virDomainObjPtr vm,
fbe740
                                        virStorageSourcePtr elem,
fbe740
                                        bool readonly,
fbe740
-                                       bool newSource);
fbe740
+                                       bool newSource,
fbe740
+                                       bool chainTop);
fbe740
 
fbe740
 int qemuDomainPrepareStorageSourceBlockdev(virDomainDiskDefPtr disk,
fbe740
                                            virStorageSourcePtr src,
fbe740
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
fbe740
index d346446444..26f100177b 100644
fbe740
--- a/src/qemu/qemu_driver.c
fbe740
+++ b/src/qemu/qemu_driver.c
fbe740
@@ -15467,7 +15467,8 @@ qemuDomainSnapshotDiskPrepareOne(virQEMUDriverPtr driver,
fbe740
     }
fbe740
 
fbe740
     /* set correct security, cgroup and locking options on the new image */
fbe740
-    if (qemuDomainStorageSourceAccessAllow(driver, vm, dd->src, false, true) < 0)
fbe740
+    if (qemuDomainStorageSourceAccessAllow(driver, vm, dd->src,
fbe740
+                                           false, true, true) < 0)
fbe740
         return -1;
fbe740
 
fbe740
     dd->prepared = true;
fbe740
@@ -18815,11 +18816,19 @@ qemuDomainBlockCommit(virDomainPtr dom,
fbe740
      * operation succeeds, but doing that requires tracking the
fbe740
      * operation in XML across libvirtd restarts.  */
fbe740
     clean_access = true;
fbe740
-    if (qemuDomainStorageSourceAccessAllow(driver, vm, baseSource, false, false) < 0 ||
fbe740
-        (top_parent && top_parent != disk->src &&
fbe740
-         qemuDomainStorageSourceAccessAllow(driver, vm, top_parent, false, false) < 0))
fbe740
+    if (qemuDomainStorageSourceAccessAllow(driver, vm, baseSource,
fbe740
+                                           false, false, false) < 0)
fbe740
         goto endjob;
fbe740
 
fbe740
+    if (top_parent && top_parent != disk->src) {
fbe740
+        /* While top_parent is topmost image, we don't need to remember its
fbe740
+         * owner as it will be overwritten upon finishing the commit. Hence,
fbe740
+         * pass chainTop = false. */
fbe740
+        if (qemuDomainStorageSourceAccessAllow(driver, vm, top_parent,
fbe740
+                                               false, false, false) < 0)
fbe740
+            goto endjob;
fbe740
+    }
fbe740
+
fbe740
     if (!(job = qemuBlockJobDiskNewCommit(vm, disk, top_parent, topSource,
fbe740
                                           baseSource,
fbe740
                                           flags & VIR_DOMAIN_BLOCK_COMMIT_DELETE,
fbe740
@@ -18877,9 +18886,11 @@ qemuDomainBlockCommit(virDomainPtr dom,
fbe740
         virErrorPtr orig_err;
fbe740
         virErrorPreserveLast(&orig_err);
fbe740
         /* Revert access to read-only, if possible.  */
fbe740
-        qemuDomainStorageSourceAccessAllow(driver, vm, baseSource, true, false);
fbe740
+        qemuDomainStorageSourceAccessAllow(driver, vm, baseSource,
fbe740
+                                           true, false, false);
fbe740
         if (top_parent && top_parent != disk->src)
fbe740
-            qemuDomainStorageSourceAccessAllow(driver, vm, top_parent, true, false);
fbe740
+            qemuDomainStorageSourceAccessAllow(driver, vm, top_parent,
fbe740
+                                               true, false, false);
fbe740
 
fbe740
         virErrorRestore(&orig_err);
fbe740
     }
fbe740
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
fbe740
index 0476f18517..dffff04554 100644
fbe740
--- a/src/qemu/qemu_process.c
fbe740
+++ b/src/qemu/qemu_process.c
fbe740
@@ -7823,7 +7823,7 @@ qemuProcessRefreshLegacyBlockjob(void *payload,
fbe740
                 (qemuDomainNamespaceSetupDisk(vm, disk->mirror) < 0 ||
fbe740
                  qemuSetupImageChainCgroup(vm, disk->mirror) < 0 ||
fbe740
                  qemuSecuritySetImageLabel(priv->driver, vm, disk->mirror,
fbe740
-                                           true) < 0))
fbe740
+                                           true, true) < 0))
fbe740
                 goto cleanup;
fbe740
         }
fbe740
     }
fbe740
diff --git a/src/qemu/qemu_security.c b/src/qemu/qemu_security.c
fbe740
index 2aa2b5b9c6..484fc34552 100644
fbe740
--- a/src/qemu/qemu_security.c
fbe740
+++ b/src/qemu/qemu_security.c
fbe740
@@ -98,7 +98,8 @@ int
fbe740
 qemuSecuritySetImageLabel(virQEMUDriverPtr driver,
fbe740
                           virDomainObjPtr vm,
fbe740
                           virStorageSourcePtr src,
fbe740
-                          bool backingChain)
fbe740
+                          bool backingChain,
fbe740
+                          bool chainTop)
fbe740
 {
fbe740
     qemuDomainObjPrivatePtr priv = vm->privateData;
fbe740
     pid_t pid = -1;
fbe740
@@ -108,6 +109,9 @@ qemuSecuritySetImageLabel(virQEMUDriverPtr driver,
fbe740
     if (backingChain)
fbe740
         labelFlags |= VIR_SECURITY_DOMAIN_IMAGE_LABEL_BACKING_CHAIN;
fbe740
 
fbe740
+    if (chainTop)
fbe740
+        labelFlags |= VIR_SECURITY_DOMAIN_IMAGE_PARENT_CHAIN_TOP;
fbe740
+
fbe740
     if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
fbe740
         pid = vm->pid;
fbe740
 
fbe740
diff --git a/src/qemu/qemu_security.h b/src/qemu/qemu_security.h
fbe740
index a8c648ece1..c8516005ac 100644
fbe740
--- a/src/qemu/qemu_security.h
fbe740
+++ b/src/qemu/qemu_security.h
fbe740
@@ -36,7 +36,8 @@ void qemuSecurityRestoreAllLabel(virQEMUDriverPtr driver,
fbe740
 int qemuSecuritySetImageLabel(virQEMUDriverPtr driver,
fbe740
                               virDomainObjPtr vm,
fbe740
                               virStorageSourcePtr src,
fbe740
-                              bool backingChain);
fbe740
+                              bool backingChain,
fbe740
+                              bool chainTop);
fbe740
 
fbe740
 int qemuSecurityRestoreImageLabel(virQEMUDriverPtr driver,
fbe740
                                   virDomainObjPtr vm,
fbe740
-- 
fbe740
2.25.1
fbe740