File libsoup-CVE-2026-0716.patch of Package libsoup

From f15b82082e5c885bdf315921a3925bcbf1905190 Mon Sep 17 00:00:00 2001
From: Mike Gorse <mgorse@suse.com>
Date: Tue, 3 Mar 2026 21:39:35 -0600
Subject: [PATCH] websocket: Fix out-of-bounds read when reading unmasked frame

The original fix for CVE-2026-0716 was incomplete; the same out-of-bounds
read can occur if a server sends a malicious unmasked frame to the client.

Closes #476
---
 libsoup/websocket/soup-websocket-connection.c | 12 ++--
 tests/websocket-test.c                        | 61 +++++++++++++++++--
 2 files changed, 62 insertions(+), 11 deletions(-)

diff --git a/libsoup/websocket/soup-websocket-connection.c b/libsoup/websocket/soup-websocket-connection.c
index 04837646..2f031f1d 100644
--- a/libsoup/websocket/soup-websocket-connection.c
+++ b/libsoup/websocket/soup-websocket-connection.c
@@ -1146,17 +1146,17 @@ process_frame (SoupWebsocketConnection *self)
 
 	payload = header + at;
 
+	/* at has a maximum value of 10 + 4 = 14 */
+	if (payload_len > G_MAXSIZE - 14) {
+		bad_data_error_and_close (self);
+		return FALSE;
+	}
+
 	if (masked) {
 		mask = header + at;
 		payload += 4;
 		at += 4;
 
-		/* at has a maximum value of 10 + 4 = 14 */
-		if (payload_len > G_MAXSIZE - 14) {
-			bad_data_error_and_close (self);
-			return FALSE;
-		}
-
 		if (len < at + payload_len)
 			return FALSE; /* need more data */
 
diff --git a/tests/websocket-test.c b/tests/websocket-test.c
index 67b9e49d..d48a9694 100644
--- a/tests/websocket-test.c
+++ b/tests/websocket-test.c
@@ -2392,7 +2392,7 @@ test_fragment_assembly_corruption (Test *test, gconstpointer data)
 }
 
 static void
-test_cve_2026_0716 (Test *test,
+test_bad_length_masked (Test *test,
                     gconstpointer unused)
 {
 	GError *error = NULL;
@@ -2426,6 +2426,53 @@ test_cve_2026_0716 (Test *test,
 	g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_BAD_DATA);
 }
 
+static gpointer
+send_bad_length_frame_server_thread (gpointer user_data)
+{
+	Test *test = user_data;
+	const char frame[] = "\x82\x7f\xff\xff\xff\xff\xff\xff\xff\xf6";
+	gsize written;
+	GError *error = NULL;
+
+	g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
+				   frame, sizeof (frame), &written, NULL, &error);
+	g_assert_no_error (error);
+	g_assert_cmpuint (written, ==, sizeof (frame));
+
+	g_io_stream_close (test->raw_server, NULL, &error);
+	g_assert_no_error (error);
+
+	return NULL;
+}
+
+static void
+test_bad_length_unmasked (Test *test,
+                    gconstpointer unused)
+{
+	GThread *thread;
+	GBytes *received = NULL;
+	GError *error = NULL;
+
+	g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
+	g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received);
+
+	soup_websocket_connection_set_max_incoming_payload_size (test->client, 0);
+
+	thread = g_thread_new ("send-bad-length-frame-thread", send_bad_length_frame_server_thread, test);
+
+	WAIT_UNTIL (error != NULL || received != NULL);
+	g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_BAD_DATA);
+	g_clear_error (&error);
+	g_assert_null (received);
+
+	/* it can emit more errors while joining the thread, thus disconnect, to avoid memory leak */
+	g_signal_handlers_disconnect_by_func (test->client, G_CALLBACK (on_error_copy), &error);
+
+        g_thread_join (thread);
+
+	WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
+}
+
 int
 main (int argc,
       char *argv[])
@@ -2717,14 +2764,18 @@ main (int argc,
                     test_fragment_assembly_corruption,
                     teardown_direct_connection);
 
-	g_test_add ("/websocket/direct/cve-2026-0716", Test, NULL,
+	g_test_add ("/websocket/direct/bad-length-masked", Test, NULL,
 		    setup_direct_connection,
-		    test_cve_2026_0716,
+		    test_bad_length_masked,
 		    teardown_direct_connection);
-	g_test_add ("/websocket/soup/cve-2026-0716", Test, NULL,
+	g_test_add ("/websocket/soup/bad-length-masked", Test, NULL,
 		    setup_soup_connection,
-		    test_cve_2026_0716,
+		    test_bad_length_masked,
 		    teardown_soup_connection);
+	g_test_add ("/websocket/direct/bad-length-unmasked", Test, NULL,
+		    setup_half_direct_connection,
+		    test_bad_length_unmasked,
+		    teardown_direct_connection);
 
 	ret = g_test_run ();
 
-- 
GitLab

openSUSE Build Service is sponsored by