File 0001-Fix-format-detection-in-libvirt-snapshot.patch of Package openstack-nova

From 592e7f3f34232c756871106669ed9ba7b31329bb Mon Sep 17 00:00:00 2001
From: Matthew Booth <mbooth@redhat.com>
Date: Wed, 9 Dec 2015 15:36:32 +0000
Subject: [PATCH 1/3] Fix format detection in libvirt snapshot

The libvirt driver was using automatic format detection during
snapshot for disks stored on the local filesystem. This opened an
exploit if nova was configured to use local file storage, and
additionally to store those files in raw format by specifying
use_cow_images = False in nova.conf. An authenticated user could write
a qcow2 header to their guest image with a backing file on the host.
libvirt.utils.get_disk_type() would then misdetect the type of this
image as qcow2 and pass this to the Qcow2 image backend, whose
snapshot_extract method interprets the image as qcow2 and writes the
backing file to glance. The authenticated user can then download the
host file from glance.

This patch makes 2 principal changes. libvirt.utils.get_disk_type,
which ought to be removed entirely as soon as possible, is updated to
no longer do format detection if the format can't be determined from
the path. Its name is changed to get_disk_type_from_path to reflect
its actual function.

libvirt.utils.find_disk is updated to return both the path and format
of the root disk, rather than just the path. This is the most reliable
source of this information, as it reflects the actual format in use.
The previous format detection function of get_disk_type is replaced by
the format taken from libvirt.

We replace a call to get_disk_type in _rebase_with_qemu_img with an
explicit call to qemu_img_info, as the other behaviour of
get_disk_type was not relevant in this context. qemu_img_info is safe
from the backing file exploit when called on a file known to be a
qcow2 image. As the file in this context is a volume snapshot, this is
a safe use.

Partial-Bug: #1524274

Conflicts:
    nova/tests/unit/virt/libvirt/test_driver.py
    nova/tests/unit/virt/libvirt/test_utils.py
    nova/virt/libvirt/driver.py
    nova/virt/libvirt/utils.py

Change-Id: I94c1c0d26215c061f71c3f95e1a6bf3a58fa19ea
(cherry picked from commit 7534182e7f1ae466f96fdb13d3715a458f300604)
---
 nova/virt/libvirt/driver.py | 25 +++++++++++++++++++------
 nova/virt/libvirt/utils.py  | 26 +++++++++++++++++++++-----
 2 files changed, 40 insertions(+), 11 deletions(-)

diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index beb5332..11c5dee 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -1681,10 +1681,23 @@ class LibvirtDriver(driver.ComputeDriver):
 
         snapshot = self._image_api.get(context, image_id)
 
-        disk_path = libvirt_utils.find_disk(virt_dom)
-        source_format = libvirt_utils.get_disk_type(disk_path)
-
-        image_format = CONF.libvirt.snapshot_image_format or source_format
+        # source_format is an on-disk format
+        # source_type is a backend type
+        disk_path, source_format = libvirt_utils.find_disk(virt_dom)
+        source_type = libvirt_utils.get_disk_type_from_path(disk_path)
+
+        # We won't have source_type for raw or qcow2 disks, because we can't
+        # determine that from the path. We should have it from the libvirt
+        # xml, though.
+        if source_type is None:
+            source_type = source_format
+        # For lxc instances we won't have it either from libvirt xml
+        # (because we just gave libvirt the mounted filesystem), or the path,
+        # so source_type is still going to be None. In this case,
+        # snapshot_backend is going to default to CONF.libvirt.images_type
+        # below, which is still safe.
+
+        image_format = CONF.libvirt.snapshot_image_format or source_type
 
         # NOTE(bfilippov): save lvm and rbd as raw
         if image_format == 'lvm' or image_format == 'rbd':
@@ -1710,7 +1723,7 @@ class LibvirtDriver(driver.ComputeDriver):
         if (self._has_min_version(MIN_LIBVIRT_LIVESNAPSHOT_VERSION,
                                   MIN_QEMU_LIVESNAPSHOT_VERSION,
                                   REQ_HYPERVISOR_LIVESNAPSHOT)
-             and source_format not in ('lvm', 'rbd')
+             and source_type not in ('lvm', 'rbd')
              and not CONF.ephemeral_storage_encryption.enabled):
             live_snapshot = True
             # Abort is an idempotent operation, so make sure any block
@@ -1744,7 +1757,7 @@ class LibvirtDriver(driver.ComputeDriver):
 
         snapshot_backend = self.image_backend.snapshot(instance,
                 disk_path,
-                image_type=source_format)
+                image_type=source_type)
 
         if live_snapshot:
             LOG.info(_LI("Beginning live snapshot process"),
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
index 6f66107..f890dfc 100644
--- a/nova/virt/libvirt/utils.py
+++ b/nova/virt/libvirt/utils.py
@@ -403,13 +403,20 @@ def find_disk(virt_dom):
     """
     xml_desc = virt_dom.XMLDesc(0)
     domain = etree.fromstring(xml_desc)
+    driver = None
     if CONF.libvirt.virt_type == 'lxc':
-        source = domain.find('devices/filesystem/source')
+        filesystem = domain.find('devices/filesystem')
+        driver = filesystem.find('driver')
+
+        source = filesystem.find('source')
         disk_path = source.get('dir')
         disk_path = disk_path[0:disk_path.rfind('rootfs')]
         disk_path = os.path.join(disk_path, 'disk')
     else:
-        source = domain.find('devices/disk/source')
+        disk = domain.find('devices/disk')
+        driver = disk.find('driver')
+
+        source = disk.find('source')
         disk_path = source.get('file') or source.get('dev')
         if not disk_path and CONF.libvirt.images_type == 'rbd':
             disk_path = source.get('name')
@@ -420,17 +427,26 @@ def find_disk(virt_dom):
         raise RuntimeError(_("Can't retrieve root device path "
                              "from instance libvirt configuration"))
 
-    return disk_path
+    if driver is not None:
+        format = driver.get('type')
+        # This is a legacy quirk of libvirt/xen. Everything else should
+        # report the on-disk format in type.
+        if format == 'aio':
+            format = 'raw'
+    else:
+        format = None
+    return (disk_path, format)
 
 
-def get_disk_type(path):
+def get_disk_type_from_path(path):
     """Retrieve disk type (raw, qcow2, lvm) for given file."""
     if path.startswith('/dev'):
         return 'lvm'
     elif path.startswith('rbd:'):
         return 'rbd'
 
-    return images.qemu_img_info(path).file_format
+    # We can't reliably determine the type from this path
+    return None
 
 
 def get_fs_info(path):
-- 
2.6.2

openSUSE Build Service is sponsored by