libvhost-user: protect against OOB vring queue access
SET_VRING_NUM, SET_VRING_ADDR, SET_VRING_BASE, and GET_VRING_BASE handlers all use the queue index from the message to access dev->vq[] without checking that it is below dev->max_queues, so a malformed message causes an out-of-bounds heap access. Frontend is trusted so not a security problem, but an OOB access is not a nice way to handle errors. Check, and panic. Fixes: 7b2e5c65f4 ("contrib: add libvhost-user") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3741 Cc: Stefano Garzarella <sgarzare@redhat.com> Reported-by: xlabai <xlabai@tencent.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <dbba777b25f86587c8d131891a2b4b2be97e5c5b.1784899069.git.mst@redhat.com>
Michael S. Tsirkin committed
Jul 8, 2026 at 11:33 UTC
758ef96a2dba157e930af7709f327435a153f7a3
1 file changed
+27
-1
subprojects/libvhost-user/libvhost-user.c
+27
-1
@@ -1200,6 +1200,12 @@ vu_set_vring_num_exec(VuDev *dev, VhostUserMsg *vmsg)
1200
1201
DPRINT("State.index: %u\n", index);
1202
DPRINT("State.num: %u\n", num);
1203
+
1204
+ if (index >= dev->max_queues) {
1205
+ vu_panic(dev, "Invalid vring_num index: %u", index);
1206
+ return false;
1207
+ }
1208
+
1209
dev->vq[index].vring.num = num;
1210
1211
return false;
@@ -1210,7 +1216,7 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg)
1216
{
1217
struct vhost_vring_addr addr = vmsg->payload.addr, *vra = &addr;
1218
unsigned int index = vra->index;
1213
- VuVirtq *vq = &dev->vq[index];
1219
+ VuVirtq *vq;
1220
1221
DPRINT("vhost_vring_addr:\n");
1222
DPRINT(" index: %d\n", vra->index);
@@ -1220,6 +1226,12 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg)
1226
DPRINT(" avail_user_addr: 0x%016" PRIx64 "\n", (uint64_t)vra->avail_user_addr);
1227
DPRINT(" log_guest_addr: 0x%016" PRIx64 "\n", (uint64_t)vra->log_guest_addr);
1228
1229
+ if (index >= dev->max_queues) {
1230
+ vu_panic(dev, "Invalid vring_addr index: %u", index);
1231
+ return false;
1232
+ }
1233
+
1234
+ vq = &dev->vq[index];
1235
vq->vra = *vra;
1236
vq->vring.flags = vra->flags;
1237
vq->vring.log_guest_addr = vra->log_guest_addr;
@@ -1256,6 +1268,12 @@ vu_set_vring_base_exec(VuDev *dev, VhostUserMsg *vmsg)
1268
1269
DPRINT("State.index: %u\n", index);
1270
DPRINT("State.num: %u\n", num);
1271
+
1272
+ if (index >= dev->max_queues) {
1273
+ vu_panic(dev, "Invalid vring_base index: %u", index);
1274
+ return false;
1275
+ }
1276
+
1277
dev->vq[index].shadow_avail_idx = dev->vq[index].last_avail_idx = num;
1278
1279
return false;
@@ -1267,6 +1285,14 @@ vu_get_vring_base_exec(VuDev *dev, VhostUserMsg *vmsg)
1285
unsigned int index = vmsg->payload.state.index;
1286
1287
DPRINT("State.index: %u\n", index);
1288
+
1289
+ if (index >= dev->max_queues) {
1290
+ vu_panic(dev, "Invalid vring_base index: %u", index);
1291
+ vmsg->payload.state.num = 0;
1292
+ vmsg->size = sizeof(vmsg->payload.state);
1293
+ return true;
1294
+ }
1295
+
1296
vmsg->payload.state.num = dev->vq[index].last_avail_idx;
1297
vmsg->size = sizeof(vmsg->payload.state);
1298