VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46036

CVE-2026-46036

Description

In the Linux kernel, the following vulnerability has been resolved:

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

vfio_cdx_set_msi_trigger() reads vdev->config_msi and operates on the vdev->cdx_irqs array based on its value, but provides no serialization against concurrent VFIO_DEVICE_SET_IRQS ioctls. Two callers can race such that one observes config_msi as set while another clears it and frees cdx_irqs via vfio_cdx_msi_disable(), resulting in a use-after-free of the cdx_irqs array.

Add a cdx_irqs_lock mutex to struct vfio_cdx_device and acquire it in vfio_cdx_set_msi_trigger(), which is the single chokepoint through which all updates to config_msi, cdx_irqs, and msi_count flow, covering both the ioctl path and the close-device cleanup path. This keeps the test of config_msi atomic with the subsequent enable, disable, or trigger operations.

Drop the pre-call !cdx_irqs test from vfio_cdx_irqs_cleanup() as part of this change: the optimization it provided is redundant with the !config_msi early-return inside vfio_cdx_msi_disable(), and leaving the test in place would be an unsynchronized read of state the new lock is meant to protect.

Affected products

2

Patches

8
670e8864b1a2

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAlex WilliamsonApr 17, 2026Fixed in 7.1-rc1via kernel-cna
3 files changed · +24 8
  • drivers/vfio/cdx/intr.c+2 7 modified
    diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c
    index c0eed065e8ef71..6dfe0ced3bdd8a 100644
    --- a/drivers/vfio/cdx/intr.c
    +++ b/drivers/vfio/cdx/intr.c
    @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
     	if (start + count > cdx_dev->num_msi)
     		return -EINVAL;
     
    +	guard(mutex)(&vdev->cdx_irqs_lock);
    +
     	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
     		vfio_cdx_msi_disable(vdev);
     		return 0;
    @@ -210,12 +212,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
     /* Free All IRQs for the given device */
     void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
     {
    -	/*
    -	 * Device does not support any interrupt or the interrupts
    -	 * were not configured
    -	 */
    -	if (!vdev->cdx_irqs)
    -		return;
    -
     	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
     }
    
  • drivers/vfio/cdx/main.c+19 0 modified
    diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c
    index 8ab97405b2bdda..b31ed4be7bdc19 100644
    --- a/drivers/vfio/cdx/main.c
    +++ b/drivers/vfio/cdx/main.c
    @@ -8,6 +8,23 @@
     
     #include "private.h"
     
    +static int vfio_cdx_init_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_init(&vdev->cdx_irqs_lock);
    +	return 0;
    +}
    +
    +static void vfio_cdx_release_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_destroy(&vdev->cdx_irqs_lock);
    +}
    +
     static int vfio_cdx_open_device(struct vfio_device *core_vdev)
     {
     	struct vfio_cdx_device *vdev =
    @@ -273,6 +290,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev,
     
     static const struct vfio_device_ops vfio_cdx_ops = {
     	.name		= "vfio-cdx",
    +	.init		= vfio_cdx_init_dev,
    +	.release	= vfio_cdx_release_dev,
     	.open_device	= vfio_cdx_open_device,
     	.close_device	= vfio_cdx_close_device,
     	.ioctl		= vfio_cdx_ioctl,
    
  • drivers/vfio/cdx/private.h+3 1 modified
    diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h
    index 172e48caa3a062..94374b5fc9899c 100644
    --- a/drivers/vfio/cdx/private.h
    +++ b/drivers/vfio/cdx/private.h
    @@ -6,6 +6,8 @@
     #ifndef VFIO_CDX_PRIVATE_H
     #define VFIO_CDX_PRIVATE_H
     
    +#include <linux/mutex.h>
    +
     #define VFIO_CDX_OFFSET_SHIFT    40
     
     static inline u64 vfio_cdx_index_to_offset(u32 index)
    @@ -31,6 +33,7 @@ struct vfio_cdx_region {
     struct vfio_cdx_device {
     	struct vfio_device	vdev;
     	struct vfio_cdx_region	*regions;
    +	struct mutex		cdx_irqs_lock;
     	struct vfio_cdx_irq	*cdx_irqs;
     	u32			flags;
     #define BME_SUPPORT BIT(0)
    -- 
    cgit 1.3-korg
    
    
    
ddf96e23c366

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAlex WilliamsonApr 17, 2026Fixed in 6.12.86via kernel-cna
3 files changed · +24 8
  • drivers/vfio/cdx/intr.c+2 7 modified
    diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c
    index 986fa2a45fa40b..371a8ff01a2684 100644
    --- a/drivers/vfio/cdx/intr.c
    +++ b/drivers/vfio/cdx/intr.c
    @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
     	if (start + count > cdx_dev->num_msi)
     		return -EINVAL;
     
    +	guard(mutex)(&vdev->cdx_irqs_lock);
    +
     	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
     		vfio_cdx_msi_disable(vdev);
     		return 0;
    @@ -206,12 +208,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
     /* Free All IRQs for the given device */
     void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
     {
    -	/*
    -	 * Device does not support any interrupt or the interrupts
    -	 * were not configured
    -	 */
    -	if (!vdev->cdx_irqs)
    -		return;
    -
     	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
     }
    
  • drivers/vfio/cdx/main.c+19 0 modified
    diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c
    index 67465fad5b4bf3..4cf2e4fb02c810 100644
    --- a/drivers/vfio/cdx/main.c
    +++ b/drivers/vfio/cdx/main.c
    @@ -8,6 +8,23 @@
     
     #include "private.h"
     
    +static int vfio_cdx_init_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_init(&vdev->cdx_irqs_lock);
    +	return 0;
    +}
    +
    +static void vfio_cdx_release_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_destroy(&vdev->cdx_irqs_lock);
    +}
    +
     static int vfio_cdx_open_device(struct vfio_device *core_vdev)
     {
     	struct vfio_cdx_device *vdev =
    @@ -281,6 +298,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev,
     
     static const struct vfio_device_ops vfio_cdx_ops = {
     	.name		= "vfio-cdx",
    +	.init		= vfio_cdx_init_dev,
    +	.release	= vfio_cdx_release_dev,
     	.open_device	= vfio_cdx_open_device,
     	.close_device	= vfio_cdx_close_device,
     	.ioctl		= vfio_cdx_ioctl,
    
  • drivers/vfio/cdx/private.h+3 1 modified
    diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h
    index dc56729b3114aa..04fc00dc869263 100644
    --- a/drivers/vfio/cdx/private.h
    +++ b/drivers/vfio/cdx/private.h
    @@ -6,6 +6,8 @@
     #ifndef VFIO_CDX_PRIVATE_H
     #define VFIO_CDX_PRIVATE_H
     
    +#include <linux/mutex.h>
    +
     #define VFIO_CDX_OFFSET_SHIFT    40
     
     static inline u64 vfio_cdx_index_to_offset(u32 index)
    @@ -31,6 +33,7 @@ struct vfio_cdx_region {
     struct vfio_cdx_device {
     	struct vfio_device	vdev;
     	struct vfio_cdx_region	*regions;
    +	struct mutex		cdx_irqs_lock;
     	struct vfio_cdx_irq	*cdx_irqs;
     	u32			flags;
     #define BME_SUPPORT BIT(0)
    -- 
    cgit 1.3-korg
    
    
    
7b436ade16cc

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAlex WilliamsonApr 17, 2026Fixed in 6.18.27via kernel-cna
3 files changed · +24 8
  • drivers/vfio/cdx/intr.c+2 7 modified
    diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c
    index 986fa2a45fa40b..371a8ff01a2684 100644
    --- a/drivers/vfio/cdx/intr.c
    +++ b/drivers/vfio/cdx/intr.c
    @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
     	if (start + count > cdx_dev->num_msi)
     		return -EINVAL;
     
    +	guard(mutex)(&vdev->cdx_irqs_lock);
    +
     	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
     		vfio_cdx_msi_disable(vdev);
     		return 0;
    @@ -206,12 +208,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
     /* Free All IRQs for the given device */
     void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
     {
    -	/*
    -	 * Device does not support any interrupt or the interrupts
    -	 * were not configured
    -	 */
    -	if (!vdev->cdx_irqs)
    -		return;
    -
     	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
     }
    
  • drivers/vfio/cdx/main.c+19 0 modified
    diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c
    index 5dd5f5ad768653..a9639730cfdf58 100644
    --- a/drivers/vfio/cdx/main.c
    +++ b/drivers/vfio/cdx/main.c
    @@ -8,6 +8,23 @@
     
     #include "private.h"
     
    +static int vfio_cdx_init_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_init(&vdev->cdx_irqs_lock);
    +	return 0;
    +}
    +
    +static void vfio_cdx_release_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_destroy(&vdev->cdx_irqs_lock);
    +}
    +
     static int vfio_cdx_open_device(struct vfio_device *core_vdev)
     {
     	struct vfio_cdx_device *vdev =
    @@ -281,6 +298,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev,
     
     static const struct vfio_device_ops vfio_cdx_ops = {
     	.name		= "vfio-cdx",
    +	.init		= vfio_cdx_init_dev,
    +	.release	= vfio_cdx_release_dev,
     	.open_device	= vfio_cdx_open_device,
     	.close_device	= vfio_cdx_close_device,
     	.ioctl		= vfio_cdx_ioctl,
    
  • drivers/vfio/cdx/private.h+3 1 modified
    diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h
    index 172e48caa3a062..94374b5fc9899c 100644
    --- a/drivers/vfio/cdx/private.h
    +++ b/drivers/vfio/cdx/private.h
    @@ -6,6 +6,8 @@
     #ifndef VFIO_CDX_PRIVATE_H
     #define VFIO_CDX_PRIVATE_H
     
    +#include <linux/mutex.h>
    +
     #define VFIO_CDX_OFFSET_SHIFT    40
     
     static inline u64 vfio_cdx_index_to_offset(u32 index)
    @@ -31,6 +33,7 @@ struct vfio_cdx_region {
     struct vfio_cdx_device {
     	struct vfio_device	vdev;
     	struct vfio_cdx_region	*regions;
    +	struct mutex		cdx_irqs_lock;
     	struct vfio_cdx_irq	*cdx_irqs;
     	u32			flags;
     #define BME_SUPPORT BIT(0)
    -- 
    cgit 1.3-korg
    
    
    
7530f34ec0ca

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAlex WilliamsonApr 17, 2026Fixed in 7.0.4via kernel-cna
3 files changed · +24 8
  • drivers/vfio/cdx/intr.c+2 7 modified
    diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c
    index 8f4402cec9c50f..0baa0a301eb252 100644
    --- a/drivers/vfio/cdx/intr.c
    +++ b/drivers/vfio/cdx/intr.c
    @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
     	if (start + count > cdx_dev->num_msi)
     		return -EINVAL;
     
    +	guard(mutex)(&vdev->cdx_irqs_lock);
    +
     	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
     		vfio_cdx_msi_disable(vdev);
     		return 0;
    @@ -206,12 +208,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
     /* Free All IRQs for the given device */
     void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
     {
    -	/*
    -	 * Device does not support any interrupt or the interrupts
    -	 * were not configured
    -	 */
    -	if (!vdev->cdx_irqs)
    -		return;
    -
     	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
     }
    
  • drivers/vfio/cdx/main.c+19 0 modified
    diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c
    index 8ab97405b2bdda..b31ed4be7bdc19 100644
    --- a/drivers/vfio/cdx/main.c
    +++ b/drivers/vfio/cdx/main.c
    @@ -8,6 +8,23 @@
     
     #include "private.h"
     
    +static int vfio_cdx_init_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_init(&vdev->cdx_irqs_lock);
    +	return 0;
    +}
    +
    +static void vfio_cdx_release_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_destroy(&vdev->cdx_irqs_lock);
    +}
    +
     static int vfio_cdx_open_device(struct vfio_device *core_vdev)
     {
     	struct vfio_cdx_device *vdev =
    @@ -273,6 +290,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev,
     
     static const struct vfio_device_ops vfio_cdx_ops = {
     	.name		= "vfio-cdx",
    +	.init		= vfio_cdx_init_dev,
    +	.release	= vfio_cdx_release_dev,
     	.open_device	= vfio_cdx_open_device,
     	.close_device	= vfio_cdx_close_device,
     	.ioctl		= vfio_cdx_ioctl,
    
  • drivers/vfio/cdx/private.h+3 1 modified
    diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h
    index 172e48caa3a062..94374b5fc9899c 100644
    --- a/drivers/vfio/cdx/private.h
    +++ b/drivers/vfio/cdx/private.h
    @@ -6,6 +6,8 @@
     #ifndef VFIO_CDX_PRIVATE_H
     #define VFIO_CDX_PRIVATE_H
     
    +#include <linux/mutex.h>
    +
     #define VFIO_CDX_OFFSET_SHIFT    40
     
     static inline u64 vfio_cdx_index_to_offset(u32 index)
    @@ -31,6 +33,7 @@ struct vfio_cdx_region {
     struct vfio_cdx_device {
     	struct vfio_device	vdev;
     	struct vfio_cdx_region	*regions;
    +	struct mutex		cdx_irqs_lock;
     	struct vfio_cdx_irq	*cdx_irqs;
     	u32			flags;
     #define BME_SUPPORT BIT(0)
    -- 
    cgit 1.3-korg
    
    
    
670e8864b1a2

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

3 files changed · +24 8
  • drivers/vfio/cdx/intr.c+2 7 modified
    diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c
    index c0eed065e8ef71..6dfe0ced3bdd8a 100644
    --- a/drivers/vfio/cdx/intr.c
    +++ b/drivers/vfio/cdx/intr.c
    @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
     	if (start + count > cdx_dev->num_msi)
     		return -EINVAL;
     
    +	guard(mutex)(&vdev->cdx_irqs_lock);
    +
     	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
     		vfio_cdx_msi_disable(vdev);
     		return 0;
    @@ -210,12 +212,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
     /* Free All IRQs for the given device */
     void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
     {
    -	/*
    -	 * Device does not support any interrupt or the interrupts
    -	 * were not configured
    -	 */
    -	if (!vdev->cdx_irqs)
    -		return;
    -
     	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
     }
    
  • drivers/vfio/cdx/main.c+19 0 modified
    diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c
    index 8ab97405b2bdda..b31ed4be7bdc19 100644
    --- a/drivers/vfio/cdx/main.c
    +++ b/drivers/vfio/cdx/main.c
    @@ -8,6 +8,23 @@
     
     #include "private.h"
     
    +static int vfio_cdx_init_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_init(&vdev->cdx_irqs_lock);
    +	return 0;
    +}
    +
    +static void vfio_cdx_release_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_destroy(&vdev->cdx_irqs_lock);
    +}
    +
     static int vfio_cdx_open_device(struct vfio_device *core_vdev)
     {
     	struct vfio_cdx_device *vdev =
    @@ -273,6 +290,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev,
     
     static const struct vfio_device_ops vfio_cdx_ops = {
     	.name		= "vfio-cdx",
    +	.init		= vfio_cdx_init_dev,
    +	.release	= vfio_cdx_release_dev,
     	.open_device	= vfio_cdx_open_device,
     	.close_device	= vfio_cdx_close_device,
     	.ioctl		= vfio_cdx_ioctl,
    
  • drivers/vfio/cdx/private.h+3 1 modified
    diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h
    index 172e48caa3a062..94374b5fc9899c 100644
    --- a/drivers/vfio/cdx/private.h
    +++ b/drivers/vfio/cdx/private.h
    @@ -6,6 +6,8 @@
     #ifndef VFIO_CDX_PRIVATE_H
     #define VFIO_CDX_PRIVATE_H
     
    +#include <linux/mutex.h>
    +
     #define VFIO_CDX_OFFSET_SHIFT    40
     
     static inline u64 vfio_cdx_index_to_offset(u32 index)
    @@ -31,6 +33,7 @@ struct vfio_cdx_region {
     struct vfio_cdx_device {
     	struct vfio_device	vdev;
     	struct vfio_cdx_region	*regions;
    +	struct mutex		cdx_irqs_lock;
     	struct vfio_cdx_irq	*cdx_irqs;
     	u32			flags;
     #define BME_SUPPORT BIT(0)
    -- 
    cgit 1.3-korg
    
    
    
7530f34ec0ca

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

3 files changed · +24 8
  • drivers/vfio/cdx/intr.c+2 7 modified
    diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c
    index 8f4402cec9c50f..0baa0a301eb252 100644
    --- a/drivers/vfio/cdx/intr.c
    +++ b/drivers/vfio/cdx/intr.c
    @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
     	if (start + count > cdx_dev->num_msi)
     		return -EINVAL;
     
    +	guard(mutex)(&vdev->cdx_irqs_lock);
    +
     	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
     		vfio_cdx_msi_disable(vdev);
     		return 0;
    @@ -206,12 +208,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
     /* Free All IRQs for the given device */
     void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
     {
    -	/*
    -	 * Device does not support any interrupt or the interrupts
    -	 * were not configured
    -	 */
    -	if (!vdev->cdx_irqs)
    -		return;
    -
     	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
     }
    
  • drivers/vfio/cdx/main.c+19 0 modified
    diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c
    index 8ab97405b2bdda..b31ed4be7bdc19 100644
    --- a/drivers/vfio/cdx/main.c
    +++ b/drivers/vfio/cdx/main.c
    @@ -8,6 +8,23 @@
     
     #include "private.h"
     
    +static int vfio_cdx_init_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_init(&vdev->cdx_irqs_lock);
    +	return 0;
    +}
    +
    +static void vfio_cdx_release_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_destroy(&vdev->cdx_irqs_lock);
    +}
    +
     static int vfio_cdx_open_device(struct vfio_device *core_vdev)
     {
     	struct vfio_cdx_device *vdev =
    @@ -273,6 +290,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev,
     
     static const struct vfio_device_ops vfio_cdx_ops = {
     	.name		= "vfio-cdx",
    +	.init		= vfio_cdx_init_dev,
    +	.release	= vfio_cdx_release_dev,
     	.open_device	= vfio_cdx_open_device,
     	.close_device	= vfio_cdx_close_device,
     	.ioctl		= vfio_cdx_ioctl,
    
  • drivers/vfio/cdx/private.h+3 1 modified
    diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h
    index 172e48caa3a062..94374b5fc9899c 100644
    --- a/drivers/vfio/cdx/private.h
    +++ b/drivers/vfio/cdx/private.h
    @@ -6,6 +6,8 @@
     #ifndef VFIO_CDX_PRIVATE_H
     #define VFIO_CDX_PRIVATE_H
     
    +#include <linux/mutex.h>
    +
     #define VFIO_CDX_OFFSET_SHIFT    40
     
     static inline u64 vfio_cdx_index_to_offset(u32 index)
    @@ -31,6 +33,7 @@ struct vfio_cdx_region {
     struct vfio_cdx_device {
     	struct vfio_device	vdev;
     	struct vfio_cdx_region	*regions;
    +	struct mutex		cdx_irqs_lock;
     	struct vfio_cdx_irq	*cdx_irqs;
     	u32			flags;
     #define BME_SUPPORT BIT(0)
    -- 
    cgit 1.3-korg
    
    
    
7b436ade16cc

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

3 files changed · +24 8
  • drivers/vfio/cdx/intr.c+2 7 modified
    diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c
    index 986fa2a45fa40b..371a8ff01a2684 100644
    --- a/drivers/vfio/cdx/intr.c
    +++ b/drivers/vfio/cdx/intr.c
    @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
     	if (start + count > cdx_dev->num_msi)
     		return -EINVAL;
     
    +	guard(mutex)(&vdev->cdx_irqs_lock);
    +
     	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
     		vfio_cdx_msi_disable(vdev);
     		return 0;
    @@ -206,12 +208,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
     /* Free All IRQs for the given device */
     void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
     {
    -	/*
    -	 * Device does not support any interrupt or the interrupts
    -	 * were not configured
    -	 */
    -	if (!vdev->cdx_irqs)
    -		return;
    -
     	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
     }
    
  • drivers/vfio/cdx/main.c+19 0 modified
    diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c
    index 5dd5f5ad768653..a9639730cfdf58 100644
    --- a/drivers/vfio/cdx/main.c
    +++ b/drivers/vfio/cdx/main.c
    @@ -8,6 +8,23 @@
     
     #include "private.h"
     
    +static int vfio_cdx_init_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_init(&vdev->cdx_irqs_lock);
    +	return 0;
    +}
    +
    +static void vfio_cdx_release_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_destroy(&vdev->cdx_irqs_lock);
    +}
    +
     static int vfio_cdx_open_device(struct vfio_device *core_vdev)
     {
     	struct vfio_cdx_device *vdev =
    @@ -281,6 +298,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev,
     
     static const struct vfio_device_ops vfio_cdx_ops = {
     	.name		= "vfio-cdx",
    +	.init		= vfio_cdx_init_dev,
    +	.release	= vfio_cdx_release_dev,
     	.open_device	= vfio_cdx_open_device,
     	.close_device	= vfio_cdx_close_device,
     	.ioctl		= vfio_cdx_ioctl,
    
  • drivers/vfio/cdx/private.h+3 1 modified
    diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h
    index 172e48caa3a062..94374b5fc9899c 100644
    --- a/drivers/vfio/cdx/private.h
    +++ b/drivers/vfio/cdx/private.h
    @@ -6,6 +6,8 @@
     #ifndef VFIO_CDX_PRIVATE_H
     #define VFIO_CDX_PRIVATE_H
     
    +#include <linux/mutex.h>
    +
     #define VFIO_CDX_OFFSET_SHIFT    40
     
     static inline u64 vfio_cdx_index_to_offset(u32 index)
    @@ -31,6 +33,7 @@ struct vfio_cdx_region {
     struct vfio_cdx_device {
     	struct vfio_device	vdev;
     	struct vfio_cdx_region	*regions;
    +	struct mutex		cdx_irqs_lock;
     	struct vfio_cdx_irq	*cdx_irqs;
     	u32			flags;
     #define BME_SUPPORT BIT(0)
    -- 
    cgit 1.3-korg
    
    
    
ddf96e23c366

vfio/cdx: Serialize VFIO_DEVICE_SET_IRQS with a per-device mutex

3 files changed · +24 8
  • drivers/vfio/cdx/intr.c+2 7 modified
    diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c
    index 986fa2a45fa40b..371a8ff01a2684 100644
    --- a/drivers/vfio/cdx/intr.c
    +++ b/drivers/vfio/cdx/intr.c
    @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
     	if (start + count > cdx_dev->num_msi)
     		return -EINVAL;
     
    +	guard(mutex)(&vdev->cdx_irqs_lock);
    +
     	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
     		vfio_cdx_msi_disable(vdev);
     		return 0;
    @@ -206,12 +208,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
     /* Free All IRQs for the given device */
     void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
     {
    -	/*
    -	 * Device does not support any interrupt or the interrupts
    -	 * were not configured
    -	 */
    -	if (!vdev->cdx_irqs)
    -		return;
    -
     	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
     }
    
  • drivers/vfio/cdx/main.c+19 0 modified
    diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c
    index 67465fad5b4bf3..4cf2e4fb02c810 100644
    --- a/drivers/vfio/cdx/main.c
    +++ b/drivers/vfio/cdx/main.c
    @@ -8,6 +8,23 @@
     
     #include "private.h"
     
    +static int vfio_cdx_init_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_init(&vdev->cdx_irqs_lock);
    +	return 0;
    +}
    +
    +static void vfio_cdx_release_dev(struct vfio_device *core_vdev)
    +{
    +	struct vfio_cdx_device *vdev =
    +		container_of(core_vdev, struct vfio_cdx_device, vdev);
    +
    +	mutex_destroy(&vdev->cdx_irqs_lock);
    +}
    +
     static int vfio_cdx_open_device(struct vfio_device *core_vdev)
     {
     	struct vfio_cdx_device *vdev =
    @@ -281,6 +298,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev,
     
     static const struct vfio_device_ops vfio_cdx_ops = {
     	.name		= "vfio-cdx",
    +	.init		= vfio_cdx_init_dev,
    +	.release	= vfio_cdx_release_dev,
     	.open_device	= vfio_cdx_open_device,
     	.close_device	= vfio_cdx_close_device,
     	.ioctl		= vfio_cdx_ioctl,
    
  • drivers/vfio/cdx/private.h+3 1 modified
    diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h
    index dc56729b3114aa..04fc00dc869263 100644
    --- a/drivers/vfio/cdx/private.h
    +++ b/drivers/vfio/cdx/private.h
    @@ -6,6 +6,8 @@
     #ifndef VFIO_CDX_PRIVATE_H
     #define VFIO_CDX_PRIVATE_H
     
    +#include <linux/mutex.h>
    +
     #define VFIO_CDX_OFFSET_SHIFT    40
     
     static inline u64 vfio_cdx_index_to_offset(u32 index)
    @@ -31,6 +33,7 @@ struct vfio_cdx_region {
     struct vfio_cdx_device {
     	struct vfio_device	vdev;
     	struct vfio_cdx_region	*regions;
    +	struct mutex		cdx_irqs_lock;
     	struct vfio_cdx_irq	*cdx_irqs;
     	u32			flags;
     #define BME_SUPPORT BIT(0)
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing serialization in vfio_cdx_set_msi_trigger() allows a race condition where concurrent VFIO_DEVICE_SET_IRQS ioctls can cause a use-after-free of the cdx_irqs array."

Attack vector

An attacker with access to a VFIO CDX device can issue concurrent `VFIO_DEVICE_SET_IRQS` ioctls from multiple threads. Because `vfio_cdx_set_msi_trigger()` reads `vdev->config_msi` and then operates on the `vdev->cdx_irqs` array without a lock, two racing callers can cause one thread to observe `config_msi` as set while another thread concurrently clears it and frees `cdx_irqs` via `vfio_cdx_msi_disable()`. This results in a use-after-free of the `cdx_irqs` array [patch_id=2660249]. The precondition is that the attacker can open the VFIO CDX device and send concurrent ioctl calls.

Affected code

The vulnerability resides in `drivers/vfio/cdx/intr.c` in the function `vfio_cdx_set_msi_trigger()`, which reads `vdev->config_msi` and operates on the `vdev->cdx_irqs` array without serialization. The cleanup path `vfio_cdx_irqs_cleanup()` also lacked proper synchronization. The fix adds a `cdx_irqs_lock` mutex to `struct vfio_cdx_device` in `drivers/vfio/cdx/private.h` and initializes/destroys it via new `init`/`release` callbacks in `drivers/vfio/cdx/main.c` [patch_id=2660249].

What the fix does

The patch adds a `cdx_irqs_lock` mutex to `struct vfio_cdx_device` and acquires it at the top of `vfio_cdx_set_msi_trigger()` using `guard(mutex)(&vdev->cdx_irqs_lock)`, which is the single chokepoint through which all updates to `config_msi`, `cdx_irqs`, and `msi_count` flow [patch_id=2660249]. This keeps the test of `config_msi` atomic with the subsequent enable, disable, or trigger operations. The patch also removes the unsynchronized `!vdev->cdx_irqs` early-return check from `vfio_cdx_irqs_cleanup()`, since that check would be a racy read of state the new lock is meant to protect, and the redundant check is already handled inside `vfio_cdx_msi_disable()`. New `init`/`release` callbacks are added to `vfio_cdx_ops` to initialize and destroy the mutex.

Preconditions

  • authAttacker must have access to a VFIO CDX device (e.g., via /dev/vfio/ device file)
  • inputAttacker must be able to issue concurrent VFIO_DEVICE_SET_IRQS ioctl calls from multiple threads

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.