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