* [PATCH] io: prevent unbounded read of websocket data from client
@ 2026-09-28 13:09 Daniel P. Berrangé
2026-09-28 20:09 ` Marc-André Lureau
0 siblings, 1 reply; 4+ messages in thread
From: Daniel P. Berrangé @ 2026-09-28 13:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Daniel P. Berrangé, Marc-André Lureau
The QIOChannelWebsock class wraps an underlying QIOChannel instance,
providing a websocket server handshake and packet encoding/decoding.
When a watch is added to QIOChannelWebsock, this in turn creates a watch
on the underlying QIOChannel that is always monitoring G_IO_IN, as long
as the 'encinput' buffer has space.
When this watch fires, it reads data off the wire into 'encinput' and
then decodes as much of 'encinput' as possible into the 'rawinput'
buffer.
The qio_channel_websock_set_watch method checks to see that 'encinput'
does not exceed a max limit, but fails to check that 'rawinput' size.
Since any data read into 'encinput', can usually be immediately decoded
into 'rawinput', the 'encinput' size check is largely a no-op, and so
'rawinput' will grow without bound.
This is readily seen with the chardev socket backend if enabling
websockets, connecting a client that sends data, while not running any
guest OS to consume data from the chardev via a virtual serial port.
The qio_channel_websock_read_wire() method will also unconditionally
read more data into 'encinput' if it has less than 4k present, which
is then decoded into 'rawinput'.
IOW, if qio_channel_websock_read_wire() is triggered for lots of small
reads, it will be reading and decoding more data to 'rawinput' than is
consumed. Skip reading more data if 'rawinput' is already at its max
size.
Reported-by: Aisle Research
Fixes: CVE-2026-101291
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
io/channel-websock.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/io/channel-websock.c b/io/channel-websock.c
index 461abcae487..646cd45be2a 100644
--- a/io/channel-websock.c
+++ b/io/channel-websock.c
@@ -970,7 +970,8 @@ static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc,
{
ssize_t ret;
- if (ioc->encinput.offset < 4096) {
+ if (ioc->encinput.offset < 4096 &&
+ ioc->rawinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
size_t want = 4096 - ioc->encinput.offset;
buffer_reserve(&ioc->encinput, want);
@@ -1091,6 +1092,7 @@ static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc)
cond |= G_IO_OUT;
}
if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER &&
+ ioc->rawinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER &&
!ioc->io_eof) {
cond |= G_IO_IN;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] io: prevent unbounded read of websocket data from client
2026-09-28 13:09 [PATCH] io: prevent unbounded read of websocket data from client Daniel P. Berrangé
@ 2026-09-28 20:09 ` Marc-André Lureau
2026-09-29 5:02 ` Philippe Mathieu-Daudé
2026-09-30 8:27 ` Daniel P. Berrangé
0 siblings, 2 replies; 4+ messages in thread
From: Marc-André Lureau @ 2026-09-28 20:09 UTC (permalink / raw)
To: Daniel P. Berrangé; +Cc: qemu-devel
On Mon, Sep 28, 2026 at 5:09 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> The QIOChannelWebsock class wraps an underlying QIOChannel instance,
> providing a websocket server handshake and packet encoding/decoding.
>
> When a watch is added to QIOChannelWebsock, this in turn creates a watch
> on the underlying QIOChannel that is always monitoring G_IO_IN, as long
> as the 'encinput' buffer has space.
>
> When this watch fires, it reads data off the wire into 'encinput' and
> then decodes as much of 'encinput' as possible into the 'rawinput'
> buffer.
>
> The qio_channel_websock_set_watch method checks to see that 'encinput'
> does not exceed a max limit, but fails to check that 'rawinput' size.
> Since any data read into 'encinput', can usually be immediately decoded
> into 'rawinput', the 'encinput' size check is largely a no-op, and so
> 'rawinput' will grow without bound.
>
> This is readily seen with the chardev socket backend if enabling
> websockets, connecting a client that sends data, while not running any
> guest OS to consume data from the chardev via a virtual serial port.
>
> The qio_channel_websock_read_wire() method will also unconditionally
> read more data into 'encinput' if it has less than 4k present, which
> is then decoded into 'rawinput'.
>
> IOW, if qio_channel_websock_read_wire() is triggered for lots of small
> reads, it will be reading and decoding more data to 'rawinput' than is
> consumed. Skip reading more data if 'rawinput' is already at its max
> size.
>
> Reported-by: Aisle Research
> Fixes: CVE-2026-101291
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
not sure if it's worth to add:
Fixes: 2d1d0e70cf3e ("io: add QIOChannelWebsock class")
> ---
> io/channel-websock.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/io/channel-websock.c b/io/channel-websock.c
> index 461abcae487..646cd45be2a 100644
> --- a/io/channel-websock.c
> +++ b/io/channel-websock.c
> @@ -970,7 +970,8 @@ static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc,
> {
> ssize_t ret;
>
> - if (ioc->encinput.offset < 4096) {
> + if (ioc->encinput.offset < 4096 &&
> + ioc->rawinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
> size_t want = 4096 - ioc->encinput.offset;
>
> buffer_reserve(&ioc->encinput, want);
> @@ -1091,6 +1092,7 @@ static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc)
> cond |= G_IO_OUT;
> }
> if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER &&
> + ioc->rawinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER &&
> !ioc->io_eof) {
> cond |= G_IO_IN;
> }
> --
> 2.55.0
>
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] io: prevent unbounded read of websocket data from client
2026-09-28 20:09 ` Marc-André Lureau
@ 2026-09-29 5:02 ` Philippe Mathieu-Daudé
2026-09-30 8:27 ` Daniel P. Berrangé
1 sibling, 0 replies; 4+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-29 5:02 UTC (permalink / raw)
To: Marc-André Lureau, Daniel P. Berrangé; +Cc: qemu-devel
On 28/9/26 22:09, Marc-André Lureau wrote:
> On Mon, Sep 28, 2026 at 5:09 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>>
>> The QIOChannelWebsock class wraps an underlying QIOChannel instance,
>> providing a websocket server handshake and packet encoding/decoding.
>>
>> When a watch is added to QIOChannelWebsock, this in turn creates a watch
>> on the underlying QIOChannel that is always monitoring G_IO_IN, as long
>> as the 'encinput' buffer has space.
>>
>> When this watch fires, it reads data off the wire into 'encinput' and
>> then decodes as much of 'encinput' as possible into the 'rawinput'
>> buffer.
>>
>> The qio_channel_websock_set_watch method checks to see that 'encinput'
>> does not exceed a max limit, but fails to check that 'rawinput' size.
>> Since any data read into 'encinput', can usually be immediately decoded
>> into 'rawinput', the 'encinput' size check is largely a no-op, and so
>> 'rawinput' will grow without bound.
>>
>> This is readily seen with the chardev socket backend if enabling
>> websockets, connecting a client that sends data, while not running any
>> guest OS to consume data from the chardev via a virtual serial port.
>>
>> The qio_channel_websock_read_wire() method will also unconditionally
>> read more data into 'encinput' if it has less than 4k present, which
>> is then decoded into 'rawinput'.
>>
>> IOW, if qio_channel_websock_read_wire() is triggered for lots of small
>> reads, it will be reading and decoding more data to 'rawinput' than is
>> consumed. Skip reading more data if 'rawinput' is already at its max
>> size.
>>
>> Reported-by: Aisle Research
>> Fixes: CVE-2026-101291
>> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> not sure if it's worth to add:
> Fixes: 2d1d0e70cf3e ("io: add QIOChannelWebsock class")
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
>
>> ---
>> io/channel-websock.c | 4 +++-
>> 1 file changed, 3 insertions(+), 1 deletion(-)
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] io: prevent unbounded read of websocket data from client
2026-09-28 20:09 ` Marc-André Lureau
2026-09-29 5:02 ` Philippe Mathieu-Daudé
@ 2026-09-30 8:27 ` Daniel P. Berrangé
1 sibling, 0 replies; 4+ messages in thread
From: Daniel P. Berrangé @ 2026-09-30 8:27 UTC (permalink / raw)
To: Marc-André Lureau; +Cc: qemu-devel
On Tue, Sep 29, 2026 at 12:09:35AM +0400, Marc-André Lureau wrote:
> On Mon, Sep 28, 2026 at 5:09 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
> >
> > The QIOChannelWebsock class wraps an underlying QIOChannel instance,
> > providing a websocket server handshake and packet encoding/decoding.
> >
> > When a watch is added to QIOChannelWebsock, this in turn creates a watch
> > on the underlying QIOChannel that is always monitoring G_IO_IN, as long
> > as the 'encinput' buffer has space.
> >
> > When this watch fires, it reads data off the wire into 'encinput' and
> > then decodes as much of 'encinput' as possible into the 'rawinput'
> > buffer.
> >
> > The qio_channel_websock_set_watch method checks to see that 'encinput'
> > does not exceed a max limit, but fails to check that 'rawinput' size.
> > Since any data read into 'encinput', can usually be immediately decoded
> > into 'rawinput', the 'encinput' size check is largely a no-op, and so
> > 'rawinput' will grow without bound.
> >
> > This is readily seen with the chardev socket backend if enabling
> > websockets, connecting a client that sends data, while not running any
> > guest OS to consume data from the chardev via a virtual serial port.
> >
> > The qio_channel_websock_read_wire() method will also unconditionally
> > read more data into 'encinput' if it has less than 4k present, which
> > is then decoded into 'rawinput'.
> >
> > IOW, if qio_channel_websock_read_wire() is triggered for lots of small
> > reads, it will be reading and decoding more data to 'rawinput' than is
> > consumed. Skip reading more data if 'rawinput' is already at its max
> > size.
> >
> > Reported-by: Aisle Research
> > Fixes: CVE-2026-101291
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> not sure if it's worth to add:
> Fixes: 2d1d0e70cf3e ("io: add QIOChannelWebsock class")
Doesn't hurt, so might as well.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-30 8:27 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-28 13:09 [PATCH] io: prevent unbounded read of websocket data from client Daniel P. Berrangé
2026-09-28 20:09 ` Marc-André Lureau
2026-09-29 5:02 ` Philippe Mathieu-Daudé
2026-09-30 8:27 ` Daniel P. Berrangé
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.