From: Denis V. Lunev <[email protected]>

qio_channel_websock_handshake_read() folds every negative return from
qio_channel_read() into -1. QIO_CHANNEL_ERR_BLOCK leaves errp unset, so
qio_channel_websock_handshake_io() then hands a NULL Error to
error_get_pretty() and QEMU dies.

The master channel is non-blocking and, for a wss:// client, is a TLS
channel. A G_IO_IN wakeup carrying only part of a TLS record makes
gnutls report EAGAIN, which is all it takes to reach this before the
client has authenticated.

ERR_BLOCK here means the headers are not complete yet, which is what a
0 return already tells the caller. Report it that way and keep waiting.
The watch is level triggered, so an incomplete record sitting in the
socket spins the main loop until the rest of it arrives. That is
bounded by the round trip and is what every reader layered over TLS
already does.

Fixes: 2d1d0e70cf3e ("io: add QIOChannelWebsock class")
Fixes: CVE-2026-84788
Cc: [email protected]
Cc: Daniel P. Berrangé <[email protected]>
Cc: Marc-André Lureau <[email protected]>
Reviewed-by: Daniel P. Berrangé <[email protected]>
Reviewed-by: Marc-André Lureau <[email protected]>
Signed-off-by: Denis V. Lunev <[email protected]>
Signed-off-by: Daniel P. Berrangé <[email protected]>
---
 io/channel-websock.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/io/channel-websock.c b/io/channel-websock.c
index 8f27b1f12b..461abcae48 100644
--- a/io/channel-websock.c
+++ b/io/channel-websock.c
@@ -492,6 +492,9 @@ static int 
qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
     buffer_reserve(&ioc->encinput, want);
     ret = qio_channel_read(ioc->master,
                            (char *)buffer_end(&ioc->encinput), want, errp);
+    if (ret == QIO_CHANNEL_ERR_BLOCK) {
+        return 0;
+    }
     if (ret < 0) {
         return -1;
     }
-- 
2.55.0

Reply via email to