On Mon, Sep 28, 2026 at 5:09 PM Daniel P. Berrangé <[email protected]> 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é <[email protected]>

Reviewed-by: Marc-André Lureau <[email protected]>

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
>
>

Reply via email to