mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:56:38 +00:00
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 <lvivier@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <20260622161144.2883799-1-lvivier@redhat.com>
This commit is contained in:
committed by
Michael S. Tsirkin
parent
ef02c6becf
commit
36b37f8494
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user