summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Booth <mbooth@redhat.com>2015-12-11 13:40:54 +0000
committerMatthew Booth <mbooth@redhat.com>2016-01-07 10:33:10 +0000
commitb974c6d1d5753f333d1d71f8190ddf3b4f8fbbf1 (patch)
tree9954d0369700131a5dc39aa61cfd51d2b34af135
parent3bc1295e2d5fb1376e3a5c708efe9fbf9435e6a4 (diff)
downloadnova-2015.1.3.tar.gz
Fix backing file detection in libvirt live snapshot2015.1.3
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
-rw-r--r--nova/tests/unit/virt/libvirt/fake_libvirt_utils.py4
-rw-r--r--nova/tests/unit/virt/libvirt/test_driver.py7
-rw-r--r--nova/virt/images.py8
-rw-r--r--nova/virt/libvirt/driver.py11
-rw-r--r--nova/virt/libvirt/utils.py9
5 files changed, 23 insertions, 16 deletions
diff --git a/nova/tests/unit/virt/libvirt/fake_libvirt_utils.py b/nova/tests/unit/virt/libvirt/fake_libvirt_utils.py
index ec9f0d19d8..f0fae18860 100644
--- a/nova/tests/unit/virt/libvirt/fake_libvirt_utils.py
+++ b/nova/tests/unit/virt/libvirt/fake_libvirt_utils.py
@@ -90,11 +90,11 @@ def create_cow_image(backing_file, path):
pass
-def get_disk_size(path):
+def get_disk_size(path, format=None):
return 0
-def get_disk_backing_file(path):
+def get_disk_backing_file(path, format=None):
return disk_backing_files.get(path, None)
diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py
index 5ab612b362..8930d9dea7 100644
--- a/nova/tests/unit/virt/libvirt/test_driver.py
+++ b/nova/tests/unit/virt/libvirt/test_driver.py
@@ -10957,7 +10957,7 @@ Active: 8381604 kB
mock_backing.return_value = bckfile
drvr._live_snapshot(self.context, self.test_instance, mock_dom,
- srcfile, dstfile, "qcow2", {})
+ srcfile, dstfile, "qcow2", "qcow2", {})
mock_dom.XMLDesc.assert_called_once_with(
fakelibvirt.VIR_DOMAIN_XML_INACTIVE |
@@ -10968,8 +10968,9 @@ Active: 8381604 kB
fakelibvirt.VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
fakelibvirt.VIR_DOMAIN_BLOCK_REBASE_SHALLOW)
- mock_size.assert_called_once_with(srcfile)
- mock_backing.assert_called_once_with(srcfile, basename=False)
+ mock_size.assert_called_once_with(srcfile, format="qcow2")
+ mock_backing.assert_called_once_with(srcfile, basename=False,
+ format="qcow2")
mock_create_cow.assert_called_once_with(bckfile, dltfile, 1004009)
mock_chown.assert_called_once_with(dltfile, os.getuid())
mock_snapshot.assert_called_once_with(dltfile, "qcow2",
diff --git a/nova/virt/images.py b/nova/virt/images.py
index ed86938703..c0da0545f9 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.
@@ -56,8 +56,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 3c8697d181..1b5c4b9a99 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -1429,7 +1429,8 @@ class LibvirtDriver(driver.ComputeDriver):
# NOTE(xqueralt): libvirt needs o+x in the temp directory
os.chmod(tmpdir, 0o701)
self._live_snapshot(context, instance, virt_dom, disk_path,
- out_path, image_format, base)
+ out_path, source_format, image_format,
+ base)
else:
snapshot_backend.snapshot_extract(out_path, image_format)
finally:
@@ -1539,7 +1540,7 @@ class LibvirtDriver(driver.ComputeDriver):
self._set_quiesced(context, instance, image_meta, False)
def _live_snapshot(self, context, instance, domain, disk_path, out_path,
- image_format, image_meta):
+ source_format, image_format, image_meta):
"""Snapshot an instance without downtime."""
# Save a copy of the domain's persistent XML file
xml = domain.XMLDesc(
@@ -1557,9 +1558,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 52ca3ee671..6a0f7dd30f 100644
--- a/nova/virt/libvirt/utils.py
+++ b/nova/virt/libvirt/utils.py
@@ -270,24 +270,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)