@samitouri / QOSamiQemu / commits / 758ef96a2d

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