From 3c87790a4ba141125e6ba165c478f0440e8e693e Mon Sep 17 00:00:00 2001
From: Michael Catanzaro <mcatanzaro@redhat.com>
Date: Fri, 16 May 2025 16:55:40 -0500
Subject: [PATCH 4/4] Add tests for max-incoming-packet-size and
 max-total-message-size

An even better test would verify that it's possible to send big messages
containing small packets, but libsoup doesn't offer control over packet
size, and I don't want to take the time to learn how WebSockets work to
figure out how to do that manually. Instead, I just check that both
limits work, for both client and server.

I didn't add deflate variants of these tests because I doubt that would
add valuable coverage.

Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/libsoup/-/commit/4d00b45b7eebdcfa0706b58e34c40b8a0a16015b]
CVE: CVE-2025-32049

Signed-off-by: Changqing Li <changqing.li@windriver.com>
---
 tests/websocket-test.c | 214 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 197 insertions(+), 17 deletions(-)

diff --git a/tests/websocket-test.c b/tests/websocket-test.c
index 827b041..ec1324c 100644
--- a/tests/websocket-test.c
+++ b/tests/websocket-test.c
@@ -543,16 +543,9 @@ test_send_big_packets (Test *test,
 {
 	GBytes *sent = NULL;
 	GBytes *received = NULL;
+	gulong signal_id;
 
-	g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
-
-	sent = g_bytes_new_take (g_strnfill (400, '!'), 400);
-	soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL));
-	WAIT_UNTIL (received != NULL);
-	g_assert (g_bytes_equal (sent, received));
-	g_bytes_unref (sent);
-	g_bytes_unref (received);
-	received = NULL;
+	signal_id = g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
 
 	sent = g_bytes_new_take (g_strnfill (100 * 1000, '?'), 100 * 1000);
 	soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL));
@@ -563,23 +556,174 @@ test_send_big_packets (Test *test,
 	received = NULL;
 
 	soup_websocket_connection_set_max_incoming_payload_size (test->client, 1000 * 1000 + 1);
-	g_assert (soup_websocket_connection_get_max_incoming_payload_size (test->client) == (1000 * 1000 + 1));
+	g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->client), ==, 1000 * 1000 + 1);
 	soup_websocket_connection_set_max_incoming_payload_size (test->server, 1000 * 1000 + 1);
-	g_assert (soup_websocket_connection_get_max_incoming_payload_size (test->server) == (1000 * 1000 + 1));
+	g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->server), ==, 1000 * 1000 + 1);
 
 	soup_websocket_connection_set_max_total_message_size (test->client, 1000 * 1000 + 1);
-	g_assert (soup_websocket_connection_get_max_total_message_size (test->client) == (1000 * 1000 + 1));
+	g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->client), ==, 1000 * 1000 + 1);
 	soup_websocket_connection_set_max_total_message_size (test->server, 1000 * 1000 + 1);
-	g_assert (soup_websocket_connection_get_max_total_message_size (test->server) == (1000 * 1000 + 1));
+	g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->server), ==, 1000 * 1000 + 1);
 
 	sent = g_bytes_new_take (g_strnfill (1000 * 1000, '?'), 1000 * 1000);
 	soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL));
 	WAIT_UNTIL (received != NULL);
 	g_assert (g_bytes_equal (sent, received));
+	g_bytes_unref (received);
+	received = NULL;
+
+	/* Reverse the test and send the big message to the server. */
+	g_signal_handler_disconnect (test->client, signal_id);
+	g_signal_connect (test->server, "message", G_CALLBACK (on_text_message), &received);
+
+	soup_websocket_connection_send_text (test->client, g_bytes_get_data (sent, NULL));
+	WAIT_UNTIL (received != NULL);
+	g_assert_true (g_bytes_equal (sent, received));
 	g_bytes_unref (sent);
 	g_bytes_unref (received);
 }
 
+static void
+test_send_big_packets_direct (Test *test,
+                              gconstpointer data)
+{
+   g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->client), ==, 128 * 1024);
+   g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->client), ==, 0);
+
+   g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->server), ==, 128 * 1024);
+   g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->server), ==, 0);
+
+   test_send_big_packets (test, data);
+}
+
+static void
+test_send_big_packets_soup (Test *test,
+                            gconstpointer data)
+{
+   g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->client), ==, 128 * 1024);
+   g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->client), ==, 0);
+
+   /* Max total message size defaults to 0 (unlimited), but SoupServer applies its own limit by default. */
+   g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->server), ==, 128 * 1024);
+   g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->server), ==, 128 * 1024);
+
+   test_send_big_packets (test, data);
+}
+
+static void
+test_send_exceeding_client_max_payload_size (Test *test,
+                                             gconstpointer data)
+{
+   GBytes *sent = NULL;
+   GBytes *received = NULL;
+   gboolean close_event = FALSE;
+   GError *error = NULL;
+
+   g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), &error);
+   g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event);
+
+   g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->client), ==, 128 * 1024);
+
+   soup_websocket_connection_set_max_incoming_payload_size (test->server, 0);
+   g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->server), ==, 0);
+
+   /* The message to the client is dropped due to the client's limit. */
+   sent = g_bytes_new_take (g_strnfill (1000 * 1000, '?'), 1000 * 1000);
+   soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL));
+   g_bytes_unref (sent);
+   WAIT_UNTIL (close_event);
+   g_assert_null (received);
+   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
+   g_assert_no_error (test->client_error);
+}
+
+static void
+test_send_exceeding_server_max_payload_size (Test *test,
+                                             gconstpointer data)
+{
+   GBytes *sent = NULL;
+   GBytes *received = NULL;
+   gboolean close_event = FALSE;
+   GError *error = NULL;
+
+   g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
+   g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event);
+
+   soup_websocket_connection_set_max_incoming_payload_size (test->client, 0);
+   g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->client), ==, 0);
+
+   g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->server), ==, 128 * 1024);
+
+   /* The message to the server is dropped due to the server's limit. */
+   sent = g_bytes_new_take (g_strnfill (1000 * 1000, '?'), 1000 * 1000);
+   soup_websocket_connection_send_text (test->client, g_bytes_get_data (sent, NULL));
+   g_bytes_unref (sent);
+   WAIT_UNTIL (close_event);
+   g_assert_null (received);
+   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
+   g_assert_no_error (test->client_error);
+}
+
+static void
+test_send_exceeding_client_max_message_size (Test *test,
+                                             gconstpointer data)
+{
+   GBytes *sent = NULL;
+   GBytes *received = NULL;
+   gboolean close_event = FALSE;
+   GError *error = NULL;
+
+   g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), &error);
+   g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event);
+
+   soup_websocket_connection_set_max_total_message_size (test->client, 128 * 1024);
+   g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->client), ==, 128 * 1024);
+
+   soup_websocket_connection_set_max_total_message_size (test->server, 0);
+   g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->server), ==, 0);
+
+   /* The message to the client is dropped due to the client's limit. */
+   sent = g_bytes_new_take (g_strnfill (1000 * 1000, '?'), 1000 * 1000);
+   soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL));
+   g_bytes_unref (sent);
+   WAIT_UNTIL (close_event);
+   g_assert_null (received);
+   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
+   g_assert_no_error (test->client_error);
+}
+
+static void
+test_send_exceeding_server_max_message_size (Test *test,
+                                             gconstpointer data)
+{
+   GBytes *sent = NULL;
+   GBytes *received = NULL;
+   gboolean close_event = FALSE;
+   GError *error = NULL;
+
+   g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
+   g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event);
+
+   soup_websocket_connection_set_max_total_message_size (test->client, 0);
+   g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->client), ==, 0);
+
+   /* Set the server message total message size manually, because its
+    * default is different for direct connection vs. soup connection.
+    */
+   soup_websocket_connection_set_max_total_message_size (test->server, 128 * 1024);
+   g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->server), ==, 128 * 1024);
+
+   /* The message to the server is dropped due to the server's limit. */
+   sent = g_bytes_new_take (g_strnfill (1000 * 1000, '?'), 1000 * 1000);
+   soup_websocket_connection_send_text (test->client, g_bytes_get_data (sent, NULL));
+   g_bytes_unref (sent);
+   WAIT_UNTIL (close_event);
+   g_assert_null (received);
+   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
+   g_assert_no_error (test->client_error);
+}
+
+
 static void
 test_send_empty_packets (Test *test,
 			 gconstpointer data)
@@ -2064,11 +2208,47 @@ main (int argc,
 
 	g_test_add ("/websocket/direct/send-big-packets", Test, NULL,
 		    setup_direct_connection,
-		    test_send_big_packets,
+		    test_send_big_packets_direct,
 		    teardown_direct_connection);
 	g_test_add ("/websocket/soup/send-big-packets", Test, NULL,
 		    setup_soup_connection,
-		    test_send_big_packets,
+		    test_send_big_packets_soup,
+		    teardown_soup_connection);
+
+	g_test_add ("/websocket/direct/send-exceeding-client-max-payload-size", Test, NULL,
+		    setup_direct_connection,
+		    test_send_exceeding_client_max_payload_size,
+		    teardown_direct_connection);
+	g_test_add ("/websocket/soup/send-exceeding-client-max-payload-size", Test, NULL,
+		    setup_soup_connection,
+		    test_send_exceeding_client_max_payload_size,
+		    teardown_soup_connection);
+
+	g_test_add ("/websocket/direct/send-exceeding-server-max-payload-size", Test, NULL,
+		    setup_direct_connection,
+		    test_send_exceeding_server_max_payload_size,
+		    teardown_direct_connection);
+	g_test_add ("/websocket/soup/send-exceeding-server-max-payload-size", Test, NULL,
+		    setup_soup_connection,
+		    test_send_exceeding_server_max_payload_size,
+		    teardown_soup_connection);
+
+	g_test_add ("/websocket/direct/send-exceeding-client-max-message-size", Test, NULL,
+		    setup_direct_connection,
+		    test_send_exceeding_client_max_message_size,
+		    teardown_direct_connection);
+	g_test_add ("/websocket/soup/send-exceeding-client-max-message-size", Test, NULL,
+		    setup_soup_connection,
+		    test_send_exceeding_client_max_message_size,
+		    teardown_soup_connection);
+
+	g_test_add ("/websocket/direct/send-exceeding-server-max-message-size", Test, NULL,
+		    setup_direct_connection,
+		    test_send_exceeding_server_max_message_size,
+		    teardown_direct_connection);
+	g_test_add ("/websocket/soup/send-exceeding-server-max-message-size", Test, NULL,
+		    setup_soup_connection,
+		    test_send_exceeding_server_max_message_size,
 		    teardown_soup_connection);
 
 	g_test_add ("/websocket/direct/send-empty-packets", Test, NULL,
@@ -2217,11 +2397,11 @@ main (int argc,
 
 	g_test_add ("/websocket/direct/deflate-send-big-packets", Test, NULL,
 		    setup_direct_connection_with_extensions,
-		    test_send_big_packets,
+		    test_send_big_packets_direct,
 		    teardown_direct_connection);
 	g_test_add ("/websocket/soup/deflate-send-big-packets", Test, NULL,
 		    setup_soup_connection_with_extensions,
-		    test_send_big_packets,
+		    test_send_big_packets_soup,
 		    teardown_soup_connection);
 
 	g_test_add ("/websocket/direct/deflate-send-empty-packets", Test, NULL,
-- 
2.34.1

