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
next prev 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 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.