Unbinding a virtio balloon device:
echo virtio0 > /sys/bus/virtio/drivers/virtio_balloon/unbind
triggers a NULL pointer dereference. The dmesg says:
BUG: kernel NULL pointer dereference, address:
0000000000000008
[...]
RIP: 0010:__list_del_entry_valid_or_report+0x5/0xf0
Call Trace:
<TASK>
vp_del_vqs+0x121/0x230
remove_common+0x135/0x150
virtballoon_remove+0xee/0x100
virtio_dev_remove+0x3b/0x80
device_release_driver_internal+0x187/0x2c0
unbind_store+0xb9/0xe0
kernfs_fop_write_iter.llvm.
11660790530567441834+0xf6/0x180
vfs_write+0x2a9/0x3b0
ksys_write+0x5c/0xd0
do_syscall_64+0x54/0x230
entry_SYSCALL_64_after_hwframe+0x29/0x31
[...]
</TASK>
The virtio_balloon device registers 5 queues (inflate, deflate, stats,
free_page, reporting) but only the first two are unconditional. The
stats, free_page and reporting queues are each conditional on their
respective feature bits. When any of these features are absent, the
corresponding vqs_info entry has name == NULL, creating holes in the
array.
The root cause is an indexing mismatch introduced when vq info storage
was changed to be passed as an argument. vp_find_vqs_msix() and
vp_find_vqs_intx() store the info pointer at vp_dev->vqs[i], where 'i'
is the caller's sparse array index. However, the virtqueue itself gets
vq->index assigned from queue_idx, a dense index that skips NULL
entries. When holes exist, 'i' and queue_idx diverge. Later,
vp_del_vqs() looks up info via vp_dev->vqs[vq->index] using the dense
index into the sparsely-populated array, and hits NULL.
Fix this by storing info at vp_dev->vqs[queue_idx] instead of
vp_dev->vqs[i], so the store index matches the lookup index
(vq->index). Apply the fix to both the MSIX and INTX paths.
Cc: Yichun Zhang <yichun@openresty.com>
Cc: Jiri Pirko <jiri@nvidia.com>
Cc: stable@vger.kernel.org # v6.11+
Tested-by: Yuka <yuka@umeyashiki.org>
Fixes: 89a1c435aec2 ("virtio_pci: pass vq info as an argument to vp_setup_vq()")
Signed-off-by: Ammar Faizi <ammarfaizi2@openresty.com>
Message-Id: <
20260315141808.547081-1-ammarfaizi2@openresty.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
vqs[i] = NULL;
continue;
}
- vqs[i] = vp_find_one_vq_msix(vdev, queue_idx++, vqi->callback,
+ vqs[i] = vp_find_one_vq_msix(vdev, queue_idx, vqi->callback,
vqi->name, vqi->ctx, false,
&allocated_vectors, vector_policy,
- &vp_dev->vqs[i]);
+ &vp_dev->vqs[queue_idx]);
+ queue_idx++;
if (IS_ERR(vqs[i])) {
err = PTR_ERR(vqs[i]);
goto error_find;
vqs[i] = NULL;
continue;
}
- vqs[i] = vp_setup_vq(vdev, queue_idx++, vqi->callback,
+ vqs[i] = vp_setup_vq(vdev, queue_idx, vqi->callback,
vqi->name, vqi->ctx,
- VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[i]);
+ VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[queue_idx]);
+ queue_idx++;
if (IS_ERR(vqs[i])) {
err = PTR_ERR(vqs[i]);
goto out_del_vqs;