Re: Should there be a mode in which the virtqueue -> MSI mapping is fixed?

Demi Marie Obenour <[email protected]> Sun, 5 Apr 2026 18:28:56 -0400
Newsgroups dev.linux.lists.virtio-comment
Message-ID <[email protected]>
On 4/5/26 17:50, Michael S. Tsirkin wrote:
> On Sun, Apr 05, 2026 at 05:47:19PM -0400, Demi Marie Obenour wrote:
>> On 4/5/26 17:09, Michael S. Tsirkin wrote:
>>> On Sun, Apr 05, 2026 at 04:58:39PM -0400, Demi Marie Obenour wrote:
>>>> On 4/5/26 16:15, Michael S. Tsirkin wrote:
>>>>> On Sun, Apr 05, 2026 at 01:50:25PM -0400, Demi Marie Obenour wrote:
>>>>>> On 4/4/26 20:56, Michael S. Tsirkin wrote:
>>>>>>> On Sat, Apr 04, 2026 at 05:19:41PM -0400, Demi Marie Obenour wrote:
>>>>>>>> Cloud Hypervisor's vhost-user frontend does not implement MSI-X
>>>>>>>> properly [1].  Specifically:
>>>>>>>>
>>>>>>>> 1. Reads from the Pending Bit Array (PBA) always return 0.
>>>>>>>> 2. Changes to the MSI associated with a virtqueue after the device
>>>>>>>>    is activated are ignored.
>>>>>>>>    
>>>>>>>> Amazingly, there have not been any reports of this causing breakage.
>>>>>>>> I have a fix for the first [2], which actually decreases the amount
>>>>>>>> of code.  However, the second is trickier and I'm tempted to not
>>>>>>>> bother unless it causes real-world problems.
>>>>>>>>
>>>>>>>> Are there real-world drivers that will run into either of the above
>>>>>>>> bugs?  Linux seems to only choose anything else as a fallback, which
>>>>>>>> presumably is not triggered.
>>>>>>>>
>>>>>>>> [1]: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/7813
>>>>>>>> [2]: https://github.com/cloud-hypervisor/cloud-hypervisor/pull/7963
>>>>>>>
>>>>>>> It will sometimes trigger.
>>>>>>
>>>>>> Would it be possible to provide an example?  A reproducible test
>>>>>> case would be ideal, but conditions under which this will trigger
>>>>>> are also sufficient.
>>>>>
>>>>>
>>>>> I am not sure what does "is activated" mean.
>>>>> For example, on latest Linux:
>>>>>
>>>>>         vq = vp_find_one_vq_msix(vdev, avq->vq_index, vp_modern_avq_done,
>>>>>                                  avq->name, false, true, &allocated_vectors,
>>>>>                                  vector_policy, &vp_dev->admin_vq.info);
>>>>>         if (IS_ERR(vq)) {
>>>>>                 err = PTR_ERR(vq);
>>>>>                 goto error_find;
>>>>>         }
>>>>>
>>>>>         return 0;
>>>>>
>>>>> error_find:
>>>>>         vp_del_vqs(vdev);
>>>>>         return err;
>>>>> }
>>>>>
>>>>>
>>>>> And 
>>>>>
>>>>> static void del_vq(struct virtio_pci_vq_info *info)
>>>>> {
>>>>>         struct virtqueue *vq = info->vq;
>>>>>         struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
>>>>>         struct virtio_pci_modern_device *mdev = &vp_dev->mdev;
>>>>>
>>>>>         if (vp_dev->msix_enabled)
>>>>>                 vp_modern_queue_vector(mdev, vq->index,
>>>>>                                        VIRTIO_MSI_NO_VECTOR);
>>>>>
>>>>>         if (!mdev->notify_base)
>>>>>                 pci_iounmap(mdev->pci_dev, (void __force __iomem *)vq->priv);
>>>>>
>>>>>         vring_del_virtqueue(vq);
>>>>> }
>>>>>
>>>>>
>>>>> and this happens after feature negotiation.
>>>>>
>>>>> It's before device_ready, however.
>>>>
>>>> Are there drivers that will change virtqueue => MSI-X vector mappings
>>>> after DRIVER_OK without an intervening reset?  Cloud Hypervisor
>>>> supports this for devices it implements internally, but it ignores
>>>> such changes for vhost-user devices.  Is this going to cause problems
>>>> in practice?
>>>
>>>
>>> Also yes.
>>>
>>> For example, dpdk uses this during cleanup to block interrupts:
>>> drivers/net/virtio/virtio_ethdev.c
>>>
>>>
>>> int
>>> virtio_dev_close(struct rte_eth_dev *dev)
>>> {       
>>>         struct virtio_hw *hw = dev->data->dev_private;
>>>         struct rte_eth_intr_conf *intr_conf = &dev->data->dev_conf.intr_conf;
>>>                                              
>>>         PMD_INIT_LOG(DEBUG, "virtio_dev_close");
>>>         if (rte_eal_process_type() != RTE_PROC_PRIMARY)
>>>                 return 0;
>>>
>>>         if (!hw->opened)
>>>                 return 0;
>>>         hw->opened = 0;
>>>
>>>         /* reset the NIC */
>>>         if (dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)
>>>                 VIRTIO_OPS(hw)->set_config_irq(hw, VIRTIO_MSI_NO_VECTOR);
>>>         if (intr_conf->rxq)
>>>                 virtio_queues_unbind_intr(dev);
>>>
>>>         if (intr_conf->lsc || intr_conf->rxq) {
>>>                 virtio_intr_disable(dev);
>>>                 rte_intr_efd_disable(dev->intr_handle);
>>>                 rte_intr_vec_list_free(dev->intr_handle);
>>>         }
>>>
>>>         virtio_reset(hw);
>>>         virtio_dev_free_mbufs(dev);
>>>         virtio_free_queues(hw);
>>>         virtio_free_rss(hw);
>>>
>>>         return VIRTIO_OPS(hw)->dev_close(hw);
>>> }
>>
>> What would happen if DPDK received a spurious MSI-X interrupt during
>> this time?
> 
> Could be a UAF?

Indeed it could, provided that DPDK is actually able to get the interrupt.

>> I know that this is non-compliant with the virtio specification.
>> However, a problem that will trigger in practice, or which can be
>> used for an exploit, is far more severe (and thus far more important
>> to fix) than one that does not have practical consequences.
>> -- 
>> Sincerely,
>> Demi Marie Obenour (she/her/hers)
> 
> 
> 
> You will have to read the code, and lots of old versions of it, to
> check.

Makes sense.

Right now, I'm working a spec for a new device (vhost-guest, formally
virtio-vhost-user).  It's a vhost-user server, so it is a virtio
device used by one guest VM (backend) to implement a virtio device
for use by another guest VM (frontend).  Yes, it's confusing!

The spec I am using as a basis is
<https://stefanha.github.io/virtio/vhost-user-slave.html>.  Both its
terminology and the spec itself are outdated and I'm going to be
heavily changing it before submission.  The current (unpublished)
version states that a vhost-guest device has 4 virtqueues in two pairs.
One pair is for requests from frontend to backend and responses
from backend to frontend.  The other is for requests from backend
to frontend and responses from frontend to backend.  However, the
device that the driver is implementing *also* has its own virtqueues,
and those need extra notifications.

The spec I am using as reference states that these notifications are
treated similar to existing virtqueue notifications.  In particular,
the MSI-X vector associated with them can be changed at will.
How important is it to support this in the version of the spec
I submit?  How important is it to allow the same MSI-X vector to be
used for multiple notifications?
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)
OpenPGP_0xB288B55FFF9C22C1.asc (application/pgp-keys, 7 KB)
-----BEGIN PGP PUBLIC KEY BLOCK-----

xsFNBFp+A0oBEADffj6anl9/BHhUSxGTICeVl2tob7hPDdhHNgPR4C8xlYt5q49y
B+l2nipdaq+4Gk6FZfqC825TKl7eRpUjMriwle4r3R0ydSIGcy4M6eb0IcxmuPYf
bWpr/si88QKgyGSVZ7GeNW1UnzTdhYHuFlk8dBSmB1fzhEYEk0RcJqg4AKoq6/3/
UorR+FaSuVwT7rqzGrTlscnTDlPWgRzrQ3jssesI7sZLm82E3pJSgaUoCdCOlL7M
MPCJwI8JpPlBedRpe9tfVyfu3euTPLPxwcV3L/cfWPGSL4PofBtB8NUU6QwYiQ9H
zx4xOyn67zW73/G0Q2vPPRst8LBDqlxLjbtx/WLR6h3nBc3eyuZ+q62HS1pJ5EvU
T1vjyJ1ySrqtUXWQ4XlZyoEFUfpJxJoN0A9HCxmHGVckzTRl5FMWo8TCniHynNXs
BtDQbabt7aNEOaAJdE7to0AH3T/Bvwzcp0ZJtBk0EM6YeMLtotUut7h2Bkg1b//r
6bTBswMBXVJ5H44Qf0+eKeUg7whSC9qpYOzzrm7+0r9F5u3qF8ZTx55TJc2g656C
9a1P1MYVysLvkLvS4H+crmxA/i08Tc1h+x9RRvqba4lSzZ6/Tmt60DPM5Sc4R0nS
m9BBff0Nm0bSNRS8InXdO1Aq3362QKX2NOwcL5YaStwODNyZUqF7izjK4QARAQAB
zTxEZW1pIE9iZW5vdXIgKElUTCBFbWFpbCBLZXkpIDxhdGhlbmFAaW52aXNpYmxl
dGhpbmdzbGFiLmNvbT7CwY4EEwEIADgWIQR2h02fEza6IlkHHHGyiLVf/5wiwQUC
X6YJvQIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCyiLVf/5wiwWRhD/0Y
R+YYC5Kduv/2LBgQJIygMsFiRHbR4+tWXuTFqgrxxFSlMktZ6gQrQCWe38WnOXkB
oY6n/5lSJdfnuGd2UagZ/9dkaGMUkqt+5WshLFly4BnP7pSsWReKgMP7etRTwn3S
zk1OwFx2lzY1EnnconPLfPBc6rWG2moA6l0WX+3WNR1B1ndqpl2hPSjT2jUCBWDV
rGOUSX7r5f1WgtBeNYnEXPBCUUM51pFGESmfHIXQrqFDA7nBNiIVFDJTmQzuEqIy
Jl67pKNgooij5mKzRhFKHfjLRAH4mmWZlB9UjDStAfFBAoDFHwd1HL5VQCNQdqEc
/9lZDApqWuCPadZN+pGouqLysesIYsNxUhJ7dtWOWHl0vs7/3qkWmWun/2uOJMQh
ra2u8nA9g91FbOobWqjrDd6x3ZJoGQf4zLqjmn/P514gb697788e573WN/MpQ5XI
Fl7aM2d6/GJiq6LC9T2gSUW4rbPBiqOCeiUx7Kd/sVm41p9TOA7fEG4bYddCfDsN
xaQJH6VRK3NOuBUGeL+iQEVF5Xs6Yp+U+jwvv2M5Lel3EqAYo5xXTx4ls0xaxDCu
fudcAh8CMMqx3fguSb7Mi31WlnZpk0fDuWQVNKyDP7lYpwc4nCCGNKCj622ZSocH
AcQmX28L8pJdLYacv9pU3jPy4fHcQYvmTavTqowGnM08RGVtaSBNYXJpZSBPYmVu
b3VyIChsb3ZlciBvZiBjb2RpbmcpIDxkZW1pb2Jlbm91ckBnbWFpbC5jb20+wsF4
BBMBAgAiBQJafgNKAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCyiLVf
/5wiwYa/EACv8a2+MMou9cSCNoZBQaU+fTmyzft9hUE+0d5W2UY1RY3OsjFIzm9R
/4SVccfsqOYLEo+S0vQMIIIqFEq3FCpXXwPzyimotps05VA8U3Bd7yseojFygOgK
sAMOAee2RCaDDOnoJue01dfZMzzHPO/TVdp3OvnpWipfv5G1Xg96rwbhMLE3tg6N
xwAHa31Bv4/Xq8CJOoIWvx6fcmZQpz01/lSvsYn0KrfEbTKkuUf0vM9JrCTCP2oz
VNN5BYzqaq2M4r+jmSyeXLim922VOWqGkUEQ85BSEemqrRS06IU6NtEMsF8EWt/b
hWjk/9GDKTcnpdJHTrMxTspExBiNrvpI2t+YPU5B/dJJAUxvmhFrbSIbdB8umBZs
I3AMYrEmpAbh5x7jEjoskUC7uN3o9vpg1oCLS2ePDLtAtyBtbHnkA4xGD7ar8mem
xpH9lY/i+sC6CyyIUWcUDnnagKyJP0m9ks0GLsTeOCA0bft2XA6rD6aaCnMUsndT
ctrab42CV5XypjmC4U1rPJ8JQJUh1/3P48/8sMH+3krxpJ06KNWNFaUbaMTGiltZ
7x9DngklSYrX0T+2G4kVXNmjaljwkoLahwLla2gUWwBSyofXdqyhQdwZsp01KXNQ
UCyT/Pg+aDcm/E7OMV3d4lf7g/CSxiX2GSEe6BlhSz+Lmd7ZJ3g32M1ARGVtaSBN
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
EtJuZYM5blWncBOJCoWMnBEcTEo/viU3GgcVRw==
=x94R
-----END PGP PUBLIC KEY BLOCK-----
OpenPGP_signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEEopQtqVJW1aeuo9/sszaHOrMp8lMFAmnS4ikACgkQszaHOrMp
8lOURQ//X0fcfU6cntOsWmkLy90fAWKY7q8dZ9ET5vcV8iUCt8tEmoJdS1bjDyQ6
pCtL2tZ+aeVJCYlbtn9LWH1LgEQlVKA9rhzZKrPf/3jdo+2ahE+5BcY5Ap4bA+v7
PWoiuOzVYzFXTNKlPfWgOclut3h+0DU0USnXAjj9Jjk/tKZ4e2KmxiFUcOrly48n
bz2AnTnn7n4IKnrhyb5CCj+5QjUmjXiyqlKe3alo4uQHBNf/N6AKaVqJ3hf7Vmki
mFyB2u8Zeqp/kH6zWug+fP9x8H4Ch9bbyjGhnAf/tXYVzywRVVvcRJlNCA+7zZWV
gAulQXoTkhjCHRNTBUiY9Ok0Ux/9I3TvbSSz4P1z30wwbZ+T68X8s/jkQRHlGyts
ikAmh12aKykj2xPPD51kRj4LHHTukX1fu+vsB94/PBYh5gJ0nTi0g6LhpjVGzwxE
6XC6CXX4AN5qpXLDkR5PrW01+txDv9qUCg+FTwLM2qXJMSPb7mqXSTWOa7rIbHXg
kdlAoL5kvq4IwJKRHJbIE8QOt7Y6BaPW2LUSNIFNgpZQQ3mZML+oauI6zZLFPT52
cvXMarp1MonTmL4iDz/c1xRCVRcIt8jKV/9PAM0esgSouHjwWwCZr+NtYSqTvyUH
Z4EFRuqI1MQZxEZs2kFV4eDed1VVhe7n3h8QxcLtuRR/AUz4+XA=
=KKLI
-----END PGP SIGNATURE-----