From 36b37f8494800ed9c5d4b6ef03924bd1636cb810 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 22 Jun 2026 18:11:44 +0200 Subject: [PATCH] hw/char/virtio-serial-bus: fix guest-triggerable OOM in control_out() A malicious guest can craft virtqueue descriptors with arbitrary lengths. control_out() calls iov_size() on the guest-supplied scatter-gather list and passes the result directly to g_malloc(), allowing a guest to force QEMU to attempt multi-gigabyte allocations and crash the host process. Fix this by copying at most sizeof(struct virtio_console_control) into a stack-local variable instead of allocating a buffer sized by the guest. handle_control_message() only accesses the fixed-size id, event, and value fields, so no data beyond the struct was ever needed. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3585 Signed-off-by: Laurent Vivier Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260622161144.2883799-1-lvivier@redhat.com> --- hw/char/virtio-serial-bus.c | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index cd234dc6db..c1973f0248 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -344,22 +344,16 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) } /* Guest wants to notify us of some event */ -static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) +static void handle_control_message(VirtIOSerial *vser, + struct virtio_console_control *gcpkt) { VirtIODevice *vdev = VIRTIO_DEVICE(vser); struct VirtIOSerialPort *port; VirtIOSerialPortClass *vsc; - struct virtio_console_control cpkt, *gcpkt; + struct virtio_console_control cpkt; uint8_t *buffer; size_t buffer_len; - gcpkt = buf; - - if (len < sizeof(cpkt)) { - /* The guest sent an invalid control packet */ - return; - } - cpkt.event = virtio_lduw_p(vdev, &gcpkt->event); cpkt.value = virtio_lduw_p(vdev, &gcpkt->value); @@ -457,41 +451,27 @@ static void control_in(VirtIODevice *vdev, VirtQueue *vq) static void control_out(VirtIODevice *vdev, VirtQueue *vq) { + struct virtio_console_control cpkt; VirtQueueElement *elem; VirtIOSerial *vser; - uint8_t *buf; size_t len; vser = VIRTIO_SERIAL(vdev); - len = 0; - buf = NULL; for (;;) { - size_t cur_len; - elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { break; } - cur_len = iov_size(elem->out_sg, elem->out_num); - /* - * Allocate a new buf only if we didn't have one previously or - * if the size of the buf differs - */ - if (cur_len > len) { - g_free(buf); - - buf = g_malloc(cur_len); - len = cur_len; + len = iov_to_buf(elem->out_sg, elem->out_num, 0, &cpkt, sizeof(cpkt)); + if (len == sizeof(cpkt)) { + handle_control_message(vser, &cpkt); } - iov_to_buf(elem->out_sg, elem->out_num, 0, buf, cur_len); - handle_control_message(vser, buf, cur_len); virtqueue_push(vq, elem, 0); g_free(elem); } - g_free(buf); virtio_notify(vdev, vq); }