Virtuozzo QEMU development (svt-core@virtuozzo.com)
 help / color / mirror / Atom feed
From: "Denis V. Lunev" <den@openvz.org>
To: svt-core@virtuozzo.com
Cc: andrey.drobyshev@virtuozzo.com, den@openvz.org
Subject: [PATCH hci-8.0 4/6] tests/unit: add websock handshake test #VSTOR-143316
Date: Mon, 31 Aug 2026 16:06:18 +0200	[thread overview]
Message-ID: <20260831140620.1204363-5-den@openvz.org> (raw)
In-Reply-To: <20260831140620.1204363-1-den@openvz.org>

From: Denis V. Lunev <den@openvz.org>

Check that malformed HTTP greetings are answered with an HTTP 400
rather than an empty response. The no-space case is the one which used
to leave the response buffer empty.

Cc: Daniel P. Berrangé <berrange@redhat.com>
Message-ID: <20260831100151.914178-5-den@openvz.org>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Denis V. Lunev <den@openvz.org>
---
 tests/unit/meson.build               |   1 +
 tests/unit/test-io-channel-websock.c | 105 +++++++++++++++++++++++++++
 2 files changed, 106 insertions(+)
 create mode 100644 tests/unit/test-io-channel-websock.c

diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index afe3ab569c9..64641c6bb43 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -91,6 +91,7 @@ if have_block
     'test-io-channel-command': ['io-channel-helpers.c', io],
     'test-io-channel-buffer': ['io-channel-helpers.c', io],
     'test-io-channel-null': [io],
+    'test-io-channel-websock': [io],
     'test-crypto-ivgen': [io],
     'test-crypto-afsplit': [io],
     'test-crypto-block': [io],
diff --git a/tests/unit/test-io-channel-websock.c b/tests/unit/test-io-channel-websock.c
new file mode 100644
index 00000000000..2a55a4bcdf8
--- /dev/null
+++ b/tests/unit/test-io-channel-websock.c
@@ -0,0 +1,105 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QEMU I/O channel websock test
+ *
+ * Copyright (c) 2026 Virtuozzo International GmbH
+ */
+
+#include "qemu/osdep.h"
+#include "io/channel-websock.h"
+#include "io/channel-socket.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "qemu/sockets.h"
+
+typedef struct {
+    bool finished;
+    bool failed;
+} QIOChannelWebsockHandshake;
+
+static void test_websock_handshake_done(QIOTask *task, gpointer opaque)
+{
+    QIOChannelWebsockHandshake *res = opaque;
+
+    res->finished = true;
+    res->failed = qio_task_propagate_error(task, NULL);
+}
+
+/*
+ * Drives a server-side handshake against @request and returns whatever
+ * the server wrote back, NUL terminated. The handshake is expected to
+ * fail; the point of the test is the HTTP response that goes with it.
+ */
+static char *test_websock_handshake_reply(const char *request)
+{
+    QIOChannelWebsockHandshake res = { false, false };
+    QIOChannelSocket *cli, *srv;
+    QIOChannelWebsock *wioc;
+    GMainContext *mainloop;
+    int channel[2];
+    char *reply;
+    ssize_t got;
+
+    g_assert(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0);
+
+    cli = qio_channel_socket_new_fd(channel[0], &error_abort);
+    srv = qio_channel_socket_new_fd(channel[1], &error_abort);
+    qio_channel_set_blocking(QIO_CHANNEL(srv), false, &error_abort);
+    qio_channel_set_blocking(QIO_CHANNEL(cli), false, &error_abort);
+
+    wioc = qio_channel_websock_new_server(QIO_CHANNEL(srv));
+    qio_channel_websock_handshake(wioc, test_websock_handshake_done,
+                                  &res, NULL);
+
+    qio_channel_write_all(QIO_CHANNEL(cli), request, strlen(request),
+                          &error_abort);
+
+    mainloop = g_main_context_default();
+    while (!res.finished) {
+        g_main_context_iteration(mainloop, TRUE);
+    }
+    g_assert(res.failed);
+
+    reply = g_malloc0(1024);
+    got = qio_channel_read(QIO_CHANNEL(cli), reply, 1023, &error_abort);
+    if (got > 0) {
+        reply[got] = '\0';
+    }
+
+    object_unref(OBJECT(wioc));
+    object_unref(OBJECT(srv));
+    object_unref(OBJECT(cli));
+
+    return reply;
+}
+
+static void test_websock_bad_request(const void *opaque)
+{
+    const char *request = opaque;
+    g_autofree char *reply = test_websock_handshake_reply(request);
+
+    g_assert_true(g_str_has_prefix(reply, "HTTP/1.1 400 Bad Request\r\n"));
+}
+
+int main(int argc, char **argv)
+{
+    module_call_init(MODULE_INIT_QOM);
+    g_test_init(&argc, &argv, NULL);
+
+#define TEST_BAD_REQUEST(name, request)                         \
+    g_test_add_data_func("/io/channel/websock/bad-request/" name, \
+                         request, test_websock_bad_request)
+
+    /*
+     * A greeting with no space at all used to leave the response buffer
+     * empty, which drove the handshake into a zero length write.
+     */
+    TEST_BAD_REQUEST("no-space", "stats\r\nx\r\n\r\n");
+    TEST_BAD_REQUEST("method-only", "GET\r\nx\r\n\r\n");
+    TEST_BAD_REQUEST("no-version", "GET /\r\nx\r\n\r\n");
+    TEST_BAD_REQUEST("bad-method", "POST / HTTP/1.1\r\nx: y\r\n\r\n");
+    TEST_BAD_REQUEST("bad-version", "GET / HTTP/1.0\r\nx: y\r\n\r\n");
+
+    return g_test_run();
+}
-- 
2.53.0


  parent reply	other threads:[~2026-08-31 14:06 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-31 14:06 [PATCH hci-8.0 0/6] io/channel-websock: fix an unauthenticated crash in the handshake #VSTOR-143316 Denis V. Lunev
2026-08-31 14:06 ` [PATCH hci-8.0 1/6] io/channel-socket: do not treat a zero length write as an error #VSTOR-143316 Denis V. Lunev
2026-08-31 14:06 ` [PATCH hci-8.0 2/6] io/channel-websock: send an HTTP 400 when the greeting has no space #VSTOR-143316 Denis V. Lunev
2026-08-31 14:06 ` [PATCH hci-8.0 3/6] io/channel-websock: handle a blocked write during the handshake #VSTOR-143316 Denis V. Lunev
2026-08-31 14:06 ` Denis V. Lunev [this message]
2026-08-31 14:06 ` [PATCH hci-8.0 5/6] io/channel-websock: do not lose QIO_CHANNEL_ERR_BLOCK while reading #VSTOR-143316 Denis V. Lunev
2026-08-31 14:06 ` [PATCH hci-8.0 6/6] tests/unit: cover blocked IO during the websock handshake #VSTOR-143316 Denis V. Lunev
2026-09-01 15:51 ` [PATCH hci-8.0 0/6] io/channel-websock: fix an unauthenticated crash in the " Andrey Drobyshev

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260831140620.1204363-5-den@openvz.org \
    --to=den@openvz.org \
    --cc=andrey.drobyshev@virtuozzo.com \
    --cc=svt-core@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox