On 11/20/19 12:17 PM, Peter Krempa wrote:
On Tue, Nov 19, 2019 at 14:15:12 +0100, Michal Privoznik wrote:
If user starts a blockcommit without --pivot or a blockcopy also
without --pivot then we modify access for qemu on both images and
leave it like that until pivot is executed. So far so good.
Problem is, if user instead of issuing pivot calls destroy on the
domain or if qemu dies whilst executing the block job. In this
case we don't ever clear the access we granted at the beginning.
To fix this, we need to mimic what block job code does for
aborting a block job -> remove image metadata from disk->mirror
and disk->src chains.

https://bugzilla.redhat.com/show_bug.cgi?id=1741456#c19

Signed-off-by: Michal Privoznik <mpriv...@redhat.com>
---

Diff to v2:
- updated commit message
- do more remove - for disk->src chain too
- put a comment about the importance of code placement

  src/qemu/qemu_process.c | 12 ++++++++++++
  1 file changed, 12 insertions(+)

diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 209d07cfe8..b9dd433f54 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -7615,6 +7615,18 @@ void qemuProcessStop(virQEMUDriverPtr driver,
      for (i = 0; i < vm->def->niothreadids; i++)
          vm->def->iothreadids[i]->thread_id = 0;
+ /* Do this explicitly after vm->pid is reset so that security drivers don't
+     * try to enter the domain's namespace which is non-existent by now as qemu
+     * is no longer running. */
+    for (i = 0; i < def->ndisks; i++) {
+        virDomainDiskDefPtr disk = def->disks[i];
+
+        if (disk->mirror)
+            qemuBlockRemoveImageMetadata(driver, vm, disk->dst, disk->mirror);
+
+        qemuBlockRemoveImageMetadata(driver, vm, disk->dst, disk->src);
+    }

So aren't we tracking the image metadata also for the shared subsets of
backing chain which might be used by multiple domains?

No. We are not doing that:

static int
virSecurityDACSetImageLabelInternal(virSecurityManagerPtr mgr,
                                    virDomainDefPtr def,
                                    virStorageSourcePtr src,
                                    virStorageSourcePtr parent)
{
    bool remember;
    bool is_toplevel = parent == src || parent->externalDataStore == src;

    ...

    /* We can't do restore on shared resources safely. Not even
     * with refcounting implemented in XATTRs because if there
     * was a domain running with the feature turned off the
     * refcounter in XATTRs would not reflect the actual number
     * of times the resource is in use and thus the last restore
     * on the resource (which actually restores the original
     * owner) might cut off access to the domain with the feature
     * disabled.
     * For disks, a shared resource is the whole backing chain
     * but the top layer, or read only image, or disk explicitly
     * marked as shared.
     */
    remember = is_toplevel && !src->readonly && !src->shared;

return virSecurityDACSetOwnership(mgr, src, NULL, user, group, remember);
}



static int
virSecuritySELinuxSetImageLabelInternal(virSecurityManagerPtr mgr,
                                        virDomainDefPtr def,
                                        virStorageSourcePtr src,
                                        virStorageSourcePtr parent)
{
    bool remember;
    bool is_toplevel = parent == src || parent->externalDataStore == src;
    int ret;

    ...

    /* We can't do restore on shared resources safely. Not even
     * with refcounting implemented in XATTRs because if there
     * was a domain running with the feature turned off the
     * refcounter in XATTRs would not reflect the actual number
     * of times the resource is in use and thus the last restore
     * on the resource (which actually restores the original
     * owner) might cut off access to the domain with the feature
     * disabled.
     * For disks, a shared resource is the whole backing chain
     * but the top layer, or read only image, or disk explicitly
     * marked as shared.
     */
    remember = is_toplevel && !src->readonly && !src->shared;

    ..

ret = virSecuritySELinuxSetFilecon(mgr, src->path, use_label, remember);

    ...
}


Because this
undoes everything and we do have some code which does this in the
security driver, don't we? So this either is duplicate or it's a
superset of what is done in the security driver.


So the problem here is that we call qemuDomainStorageSourceAccessAllow() but never call the counter parts. Now, I could call qemuDomainStorageSourceAccessRevoke() here but:

1) we don't need to bother with CGroups or image locking (as in virtlockd/sanlock) because qemu process is gone at this point and so is its CGroup and image locks (both virtlockd and sanlock automatically release image locks if the process holding locks dies),

2) I'd need to access top_parent and baseSource - are they available here easily?

3) we are doing this code I'm suggesting already in qemuBlockJobEventProcessLegacyCompleted()

Also note that I'm calling qemuBlockRemoveImageMetadata() only after qemuSecurityRestoreAllLabel().

Michal

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to