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