Medium severity5.3NVD Advisory· Published Apr 12, 2016· Updated May 6, 2026
CVE-2016-2140
CVE-2016-2140
Description
The libvirt driver in OpenStack Compute (Nova) before 2015.1.4 (kilo) and 12.0.x before 12.0.3 (liberty), when using raw storage and use_cow_images is set to false, allows remote authenticated users to read arbitrary files via a crafted qcow2 header in an ephemeral or root disk.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
novaPyPI | >= 12.0.0, < 12.0.3 | 12.0.3 |
Affected products
1Patches
30b194187db9dAdd release note for CVE bug 1548450
1 file changed · +7 −0
releasenotes/notes/12.0.3-cve-bugs-reno-561a450b346edf5e.yaml+7 −0 added@@ -0,0 +1,7 @@ +--- +security: + - | + [OSSA 2016-007] Host data leak during resize/migrate for raw-backed instances (CVE-2016-2140) + + * `Bug 1548450 <https://bugs.launchpad.net/nova/+bug/1548450>`_ + * `Announcement <http://lists.openstack.org/pipermail/openstack-announce/2016-March/001009.html>`__ \ No newline at end of file
116b1210ab77libvirt: Always copy or recreate disk.info during a migration
2 files changed · +102 −0
nova/tests/unit/virt/libvirt/test_driver.py+75 −0 modified@@ -8636,6 +8636,43 @@ def check_instance_dir(context, instance, fallback_from_host=instance.host)]) self.assertIsInstance(res, objects.LibvirtLiveMigrateData) + def test_pre_live_migration_recreate_disk_info(self): + + migrate_data = {'is_shared_block_storage': False, + 'is_shared_instance_path': False, + 'block_migration': True, + 'instance_relative_path': '/some/path/'} + disk_info = [{'disk_size': 5368709120, 'type': 'raw', + 'virt_disk_size': 5368709120, + 'path': '/some/path/disk', + 'backing_file': '', 'over_committed_disk_size': 0}, + {'disk_size': 1073741824, 'type': 'raw', + 'virt_disk_size': 1073741824, + 'path': '/some/path/disk.eph0', + 'backing_file': '', 'over_committed_disk_size': 0}] + image_disk_info = {'/some/path/disk': 'raw', + '/some/path/disk.eph0': 'raw'} + + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = objects.Instance(**self.test_instance) + instance_path = os.path.dirname(disk_info[0]['path']) + disk_info_path = os.path.join(instance_path, 'disk.info') + + with test.nested( + mock.patch.object(os, 'mkdir'), + mock.patch.object(fake_libvirt_utils, 'write_to_file'), + mock.patch.object(drvr, '_create_images_and_backing') + ) as ( + mkdir, write_to_file, create_images_and_backing + ): + drvr.pre_live_migration(self.context, instance, + block_device_info=None, + network_info=[], + disk_info=jsonutils.dumps(disk_info), + migrate_data=migrate_data) + write_to_file.assert_called_with(disk_info_path, + jsonutils.dumps(image_disk_info)) + def test_get_instance_disk_info_works_correctly(self): # Test data instance = objects.Instance(**self.test_instance) @@ -14170,6 +14207,44 @@ def test_migrate_disk_and_power_off_resize_error_eph(self, mock_get, flavor_obj = objects.Flavor(**flavor) self._test_migrate_disk_and_power_off(flavor_obj) + @mock.patch('nova.utils.execute') + @mock.patch('nova.virt.libvirt.utils.copy_image') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._destroy') + @mock.patch('nova.virt.libvirt.utils.get_instance_path') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver' + '._is_storage_shared_with') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver' + '.get_instance_disk_info') + def test_migrate_disk_and_power_off_resize_copy_disk_info(self, + mock_disk_info, + mock_shared, + mock_path, + mock_destroy, + mock_copy, + mock_execuate): + + instance = self._create_instance() + disk_info = self._disk_info() + disk_info_text = jsonutils.loads(disk_info) + instance_base = os.path.dirname(disk_info_text[0]['path']) + flavor = {'root_gb': 10, 'ephemeral_gb': 25} + flavor_obj = objects.Flavor(**flavor) + + mock_disk_info.return_value = disk_info + mock_path.return_value = instance_base + mock_shared.return_value = False + + self.drvr.migrate_disk_and_power_off(context.get_admin_context(), + instance, mock.sentinel, + flavor_obj, None) + + src_disk_info_path = os.path.join(instance_base + '_resize', + 'disk.info') + dst_disk_info_path = os.path.join(instance_base, 'disk.info') + mock_copy.assert_any_call(src_disk_info_path, dst_disk_info_path, + host=mock.sentinel, on_execute=mock.ANY, + on_completion=mock.ANY, compression=mock.ANY) + def test_wait_for_running(self): def fake_get_info(instance): if instance['name'] == "not_found":
nova/virt/libvirt/driver.py+27 −0 modified@@ -6655,6 +6655,24 @@ def pre_live_migration(self, context, instance, block_device_info, instance=instance) os.mkdir(instance_dir) + # Recreate the disk.info file and in doing so stop the + # imagebackend from recreating it incorrectly by inspecting the + # contents of each file when using the Raw backend. + if disk_info: + image_disk_info = {} + for info in disk_info: + image_file = os.path.basename(info['path']) + image_path = os.path.join(instance_dir, image_file) + image_disk_info[image_path] = info['type'] + + LOG.debug('Creating disk.info with the contents: %s', + image_disk_info, instance=instance) + + image_disk_info_path = os.path.join(instance_dir, + 'disk.info') + libvirt_utils.write_to_file(image_disk_info_path, + jsonutils.dumps(image_disk_info)) + if not is_shared_block_storage: # Ensure images and backing files are present. LOG.debug('Checking to make sure images and backing files are ' @@ -7242,6 +7260,15 @@ def migrate_disk_and_power_off(self, context, instance, dest, on_execute=on_execute, on_completion=on_completion, compression=compression) + + # Ensure disk.info is written to the new path to avoid disks being + # reinspected and potentially changing format. + src_disk_info_path = os.path.join(inst_base_resize, 'disk.info') + dst_disk_info_path = os.path.join(inst_base, 'disk.info') + libvirt_utils.copy_image(src_disk_info_path, dst_disk_info_path, + host=dest, on_execute=on_execute, + on_completion=on_completion, + compression=compression) except Exception: with excutils.save_and_reraise_exception(): self._cleanup_remote_migration(dest, inst_base,
f302bf04ab5dlibvirt: Always copy or recreate disk.info during a migration
2 files changed · +101 −0
nova/tests/unit/virt/libvirt/test_driver.py+75 −0 modified@@ -6608,6 +6608,43 @@ def test_pre_live_migration_block_migrate_fails(self): self.context, instance, block_device_info=None, network_info=[], disk_info={}, migrate_data={}) + def test_pre_live_migration_recreate_disk_info(self): + + migrate_data = {'is_shared_block_storage': False, + 'is_shared_instance_path': False, + 'block_migration': True, + 'instance_relative_path': '/some/path/'} + disk_info = [{'disk_size': 5368709120, 'type': 'raw', + 'virt_disk_size': 5368709120, + 'path': '/some/path/disk', + 'backing_file': '', 'over_committed_disk_size': 0}, + {'disk_size': 1073741824, 'type': 'raw', + 'virt_disk_size': 1073741824, + 'path': '/some/path/disk.eph0', + 'backing_file': '', 'over_committed_disk_size': 0}] + image_disk_info = {'/some/path/disk': 'raw', + '/some/path/disk.eph0': 'raw'} + + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = objects.Instance(**self.test_instance) + instance_path = os.path.dirname(disk_info[0]['path']) + disk_info_path = os.path.join(instance_path, 'disk.info') + + with contextlib.nested( + mock.patch.object(os, 'mkdir'), + mock.patch.object(fake_libvirt_utils, 'write_to_file'), + mock.patch.object(drvr, '_create_images_and_backing') + ) as ( + mkdir, write_to_file, create_images_and_backing + ): + drvr.pre_live_migration(self.context, instance, + block_device_info=None, + network_info=[], + disk_info=disk_info, + migrate_data=migrate_data) + write_to_file.assert_called_with(disk_info_path, + jsonutils.dumps(image_disk_info)) + def test_get_instance_disk_info_works_correctly(self): # Test data instance = objects.Instance(**self.test_instance) @@ -11607,6 +11644,44 @@ def test_migrate_disk_and_power_off_resize_error_eph(self, mock_get, flavor_obj = objects.Flavor(**flavor) self._test_migrate_disk_and_power_off(flavor_obj) + @mock.patch('nova.utils.execute') + @mock.patch('nova.virt.libvirt.utils.copy_image') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._destroy') + @mock.patch('nova.virt.libvirt.utils.get_instance_path') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver' + '._is_storage_shared_with') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver' + '.get_instance_disk_info') + def test_migrate_disk_and_power_off_resize_copy_disk_info(self, + mock_disk_info, + mock_shared, + mock_path, + mock_destroy, + mock_copy, + mock_execuate): + + instance = self._create_instance() + disk_info = self._disk_info() + disk_info_text = jsonutils.loads(disk_info) + instance_base = os.path.dirname(disk_info_text[0]['path']) + flavor = {'root_gb': 10, 'ephemeral_gb': 25} + flavor_obj = objects.Flavor(**flavor) + + mock_disk_info.return_value = disk_info + mock_path.return_value = instance_base + mock_shared.return_value = False + + self.drvr.migrate_disk_and_power_off(context.get_admin_context(), + instance, mock.sentinel, + flavor_obj, None) + + src_disk_info_path = os.path.join(instance_base + '_resize', + 'disk.info') + dst_disk_info_path = os.path.join(instance_base, 'disk.info') + mock_copy.assert_any_call(src_disk_info_path, dst_disk_info_path, + host=mock.sentinel, on_execute=mock.ANY, + on_completion=mock.ANY) + def test_wait_for_running(self): def fake_get_info(instance): if instance['name'] == "not_found":
nova/virt/libvirt/driver.py+26 −0 modified@@ -5825,6 +5825,24 @@ def pre_live_migration(self, context, instance, block_device_info, raise exception.DestinationDiskExists(path=instance_dir) os.mkdir(instance_dir) + # Recreate the disk.info file and in doing so stop the + # imagebackend from recreating it incorrectly by inspecting the + # contents of each file when using the Raw backend. + if disk_info: + image_disk_info = {} + for info in disk_info: + image_file = os.path.basename(info['path']) + image_path = os.path.join(instance_dir, image_file) + image_disk_info[image_path] = info['type'] + + LOG.debug('Creating disk.info with the contents: %s', + image_disk_info, instance=instance) + + image_disk_info_path = os.path.join(instance_dir, + 'disk.info') + libvirt_utils.write_to_file(image_disk_info_path, + jsonutils.dumps(image_disk_info)) + if not is_shared_block_storage: # Ensure images and backing files are present. self._create_images_and_backing( @@ -6362,6 +6380,14 @@ def migrate_disk_and_power_off(self, context, instance, dest, libvirt_utils.copy_image(from_path, img_path, host=dest, on_execute=on_execute, on_completion=on_completion) + + # Ensure disk.info is written to the new path to avoid disks being + # reinspected and potentially changing format. + src_disk_info_path = os.path.join(inst_base_resize, 'disk.info') + dst_disk_info_path = os.path.join(inst_base, 'disk.info') + libvirt_utils.copy_image(src_disk_info_path, dst_disk_info_path, + host=dest, on_execute=on_execute, + on_completion=on_completion) except Exception: with excutils.save_and_reraise_exception(): self._cleanup_remote_migration(dest, inst_base,
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
16- security.openstack.org/ossa/OSSA-2016-007.htmlnvdPatchVendor AdvisoryWEB
- www.openwall.com/lists/oss-security/2016/03/08/6nvdMailing ListThird Party AdvisoryWEB
- www.securityfocus.com/bid/84277nvdThird Party AdvisoryVDB EntryWEB
- bugs.launchpad.net/nova/+bug/1548450nvdThird Party AdvisoryWEB
- github.com/advisories/GHSA-49jv-37hm-6gfpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2016-2140ghsaADVISORY
- seclists.org/oss-sec/2016/q1/563ghsaWEB
- access.redhat.com/errata/RHSA-2016:0363ghsaWEB
- access.redhat.com/errata/RHSA-2016:0364ghsaWEB
- access.redhat.com/errata/RHSA-2016:0365ghsaWEB
- access.redhat.com/errata/RHSA-2016:0366ghsaWEB
- access.redhat.com/security/cve/CVE-2016-2140ghsaWEB
- bugzilla.redhat.com/show_bug.cgighsaWEB
- github.com/openstack/nova/commit/0b194187db9da28225cb5e62be3b45aff5a1c793ghsaWEB
- github.com/openstack/nova/commit/116b1210ab772c55d1ed1f715687d83877c92701ghsaWEB
- github.com/openstack/nova/commit/f302bf04ab5dda89cf8ceaeed309006da90c0666ghsaWEB
News mentions
0No linked articles in our index yet.