VYPR
High severity8.2NVD Advisory· Published Feb 18, 2026· Updated Apr 15, 2026

CVE-2026-24708

CVE-2026-24708

Description

An issue was discovered in OpenStack Nova before 30.2.2, 31 before 31.2.1, and 32 before 32.1.1. By writing a malicious QCOW header to a root or ephemeral disk and then triggering a resize, a user may convince Nova's Flat image backend to call qemu-img without a format restriction, resulting in an unsafe image resize operation that could destroy data on the host system. Only compute nodes using the Flat image backend (usually configured with use_cow_images=False) are affected.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
NovaPyPI
>= 32.0.0.0rc1, <= 32.1.0
NovaPyPI
>= 31.0.0.0rc1, <= 31.2.0
NovaPyPI
<= 30.2.1

Patches

1
3eba22ff09c8

Make disk.extend() pass format to qemu-img

https://github.com/openstack/novaDan SmithFeb 17, 2026via ghsa
2 files changed · +46 5
  • nova/tests/unit/virt/disk/test_api.py+31 4 modified
    @@ -19,6 +19,7 @@
     from oslo_concurrency import processutils
     from oslo_utils import units
     
    +from nova import exception
     from nova import test
     from nova.virt.disk import api
     from nova.virt.disk.mount import api as mount
    @@ -128,7 +129,7 @@ def test_extend_qcow_success(self, mock_exec, mock_inst, mock_resize,
     
                 mock_can_resize.assert_called_once_with(imgfile, imgsize)
                 mock_exec.assert_called_once_with('qemu-img', 'resize',
    -                                              imgfile, imgsize)
    +                                              '-f', 'qcow2', imgfile, imgsize)
                 mock_extendable.assert_called_once_with(image)
                 mock_inst.assert_called_once_with(image, None, None)
                 mock_resize.assert_called_once_with(mounter.device,
    @@ -154,8 +155,8 @@ def test_extend_qcow_no_resize(self, mock_execute, mock_extendable,
             api.extend(image, imgsize)
     
             mock_can_resize_image.assert_called_once_with(imgfile, imgsize)
    -        mock_execute.assert_called_once_with('qemu-img', 'resize', imgfile,
    -                                             imgsize)
    +        mock_execute.assert_called_once_with('qemu-img', 'resize', '-f',
    +                                             'qcow2', imgfile, imgsize)
             self.assertFalse(mock_extendable.called)
     
         @mock.patch.object(api, 'can_resize_image', autospec=True,
    @@ -186,8 +187,34 @@ def test_extend_raw_success(self, mock_exec, mock_resize,
             api.extend(image, imgsize)
     
             mock_exec.assert_has_calls(
    -            [mock.call('qemu-img', 'resize', imgfile, imgsize),
    +            [mock.call('qemu-img', 'resize', '-f', 'raw', imgfile, imgsize),
                  mock.call('e2label', image.path)])
             mock_resize.assert_called_once_with(imgfile, run_as_root=False,
                                                 check_exit_code=[0])
             mock_can_resize.assert_called_once_with(imgfile, imgsize)
    +
    +    @mock.patch.object(api, 'can_resize_image', autospec=True,
    +                       return_value=True)
    +    @mock.patch.object(api, 'resize2fs', autospec=True)
    +    @mock.patch('oslo_concurrency.processutils.execute', autospec=True)
    +    def test_extend_vmdk_failure(self, mock_exec, mock_resize,
    +                                 mock_can_resize):
    +
    +        imgfile = tempfile.NamedTemporaryFile()
    +        self.addCleanup(imgfile.close)
    +        imgsize = 10
    +        # NOTE(danms): There is no image.model.FORMAT_VMDK, but since the
    +        # code initializes this directly from Image.disk_format without using
    +        # the constant (tsk), this can actually happen at runtime.
    +        self.assertRaises(exception.InvalidImageFormat,
    +                          imgmodel.LocalFileImage, imgfile, 'vmdk')
    +
    +        # Patch ALL_FORMATS to include vmdk as if it got added at some point
    +        with mock.patch('nova.virt.image.model.ALL_FORMATS',
    +                        new=['vmdk']):
    +            image = imgmodel.LocalFileImage(imgfile, 'vmdk')
    +
    +        # Make sure that we still don't call qemu-img resize on the image
    +        self.assertRaises(exception.InvalidDiskFormat,
    +                          api.extend, image, imgsize)
    +        mock_exec.assert_not_called()
    
  • nova/virt/disk/api.py+15 1 modified
    @@ -123,7 +123,21 @@ def extend(image, size):
             nova.privsep.libvirt.ploop_resize(image.path, size)
             return
     
    -    processutils.execute('qemu-img', 'resize', image.path, size)
    +    # NOTE(danms): We should not call qemu-img without a format, and
    +    # only qcow2 and raw are supported. So check which one we're being
    +    # told this is supposed to be and pass that to qemu-img. Also note
    +    # that we need to pass the qemu format string to this command, which
    +    # may or may not be the same as the FORMAT_* constant, so be
    +    # explicit here.
    +    if image.format == imgmodel.FORMAT_RAW:
    +        format = 'raw'
    +    elif image.format == imgmodel.FORMAT_QCOW2:
    +        format = 'qcow2'
    +    else:
    +        LOG.warning('Attempting to resize image %s with format %s, '
    +        'which is not supported', image.path, image.format)
    +        raise exception.InvalidDiskFormat(disk_format=image.format)
    +    processutils.execute('qemu-img', 'resize', '-f', format, image.path, size)
     
         if (image.format != imgmodel.FORMAT_RAW and
             not CONF.resize_fs_using_block_device):
    

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

6

News mentions

0

No linked articles in our index yet.