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