From a4b3c6ac37b3b77db2e8242ae361d14d166642fc Mon Sep 17 00:00:00 2001
From: Matthew Booth <mbooth@redhat.com>
Date: Fri, 11 Dec 2015 13:40:54 +0000
Subject: [PATCH 3/3] Fix backing file detection in libvirt live snapshot

When doing a live snapshot, the libvirt driver creates an intermediate
qcow2 file with the same backing file as the original disk. However,
it calls qemu-img info without specifying the input format explicitly.
An authenticated user can write data to a raw disk which will cause
this code to misinterpret the disk as a qcow2 file with a
user-specified backing file on the host, and return an arbitrary host
file as the backing file.

This bug does not appear to result in a data leak in this case, but
this is hard to verify. It certainly results in corrupt output.

Closes-Bug: #1524274

Change-Id: I11485f077d28f4e97529a691e55e3e3c0bea8872
(cherry picked from commit b974c6d1d5753f333d1d71f8190ddf3b4f8fbbf1)
---
 nova/virt/images.py         |  8 +++++---
 nova/virt/libvirt/driver.py | 11 +++++++----
 nova/virt/libvirt/utils.py  |  9 +++++----
 3 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/nova/virt/images.py b/nova/virt/images.py
index 08a1279..810ed81 100644
--- a/nova/virt/images.py
+++ b/nova/virt/images.py
@@ -44,7 +44,7 @@ CONF.register_opts(image_opts)
 IMAGE_API = image.API()
 
 
-def qemu_img_info(path):
+def qemu_img_info(path, format=None):
     """Return an object containing the parsed output from qemu-img info."""
     # TODO(mikal): this code should not be referring to a libvirt specific
     # flag.
@@ -52,8 +52,10 @@ def qemu_img_info(path):
         msg = (_("Path does not exist %(path)s") % {'path': path})
         raise exception.InvalidDiskInfo(reason=msg)
 
-    out, err = utils.execute('env', 'LC_ALL=C', 'LANG=C',
-                             'qemu-img', 'info', path)
+    cmd = ('env', 'LC_ALL=C', 'LANG=C', 'qemu-img', 'info', path)
+    if format is not None:
+        cmd = cmd + ('-f', format)
+    out, err = utils.execute(*cmd)
     if not out:
         msg = (_("Failed to run qemu-img info on %(path)s : %(error)s") %
                {'path': path, 'error': err})
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index 11c5dee..4dbe2ed 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -1776,7 +1776,7 @@ class LibvirtDriver(driver.ComputeDriver):
                     # NOTE(xqueralt): libvirt needs o+x in the temp directory
                     os.chmod(tmpdir, 0o701)
                     self._live_snapshot(virt_dom, disk_path, out_path,
-                                        image_format)
+                                        source_format, image_format)
                 else:
                     snapshot_backend.snapshot_extract(out_path, image_format)
             finally:
@@ -1838,7 +1838,8 @@ class LibvirtDriver(driver.ComputeDriver):
 
         return not job_ended
 
-    def _live_snapshot(self, domain, disk_path, out_path, image_format):
+    def _live_snapshot(self, domain, disk_path, out_path, source_format,
+                       image_format):
         """Snapshot an instance without downtime."""
         # Save a copy of the domain's persistent XML file
         xml = domain.XMLDesc(
@@ -1856,9 +1857,11 @@ class LibvirtDriver(driver.ComputeDriver):
         #             in QEMU 1.3. In order to do this, we need to create
         #             a destination image with the original backing file
         #             and matching size of the instance root disk.
-        src_disk_size = libvirt_utils.get_disk_size(disk_path)
+        src_disk_size = libvirt_utils.get_disk_size(disk_path,
+                                                    format=source_format)
         src_back_path = libvirt_utils.get_disk_backing_file(disk_path,
-                                                            basename=False)
+                                                        format=source_format,
+                                                        basename=False)
         disk_delta = out_path + '.delta'
         libvirt_utils.create_cow_image(src_back_path, disk_delta,
                                        src_disk_size)
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
index f890dfc..c7cda44 100644
--- a/nova/virt/libvirt/utils.py
+++ b/nova/virt/libvirt/utils.py
@@ -235,24 +235,25 @@ def pick_disk_driver_name(hypervisor_version, is_block_dev=False):
         return None
 
 
-def get_disk_size(path):
+def get_disk_size(path, format=None):
     """Get the (virtual) size of a disk image
 
     :param path: Path to the disk image
+    :param format: the on-disk format of path
     :returns: Size (in bytes) of the given disk image as it would be seen
               by a virtual machine.
     """
-    size = images.qemu_img_info(path).virtual_size
+    size = images.qemu_img_info(path, format).virtual_size
     return int(size)
 
 
-def get_disk_backing_file(path, basename=True):
+def get_disk_backing_file(path, basename=True, format=None):
     """Get the backing file of a disk image
 
     :param path: Path to the disk image
     :returns: a path to the image's backing store
     """
-    backing_file = images.qemu_img_info(path).backing_file
+    backing_file = images.qemu_img_info(path, format).backing_file
     if backing_file and basename:
         backing_file = os.path.basename(backing_file)
 
-- 
2.6.2

