Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 247 additions & 0 deletions SPECS/libsoup/CVE-2025-32049.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
From c0ef6fa6df2561d9bcdf8667869fa7e84ce0eefd Mon Sep 17 00:00:00 2001
From: AllSpark <allspark@microsoft.com>
Date: Mon, 9 Feb 2026 16:06:10 +0000
Subject: [PATCH] websocket: add max-total-message-size safety valve and
property; split payload/message too-big errors; fix tests to use
g_assert_true

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: AI Backport of https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/408.patch
---
libsoup/websocket/soup-websocket-connection.c | 107 +++++++++++++++++-
libsoup/websocket/soup-websocket-connection.h | 8 ++
tests/websocket-test.c | 6 +-
3 files changed, 116 insertions(+), 5 deletions(-)

diff --git a/libsoup/websocket/soup-websocket-connection.c b/libsoup/websocket/soup-websocket-connection.c
index 5eb8150..50dcc9e 100644
--- a/libsoup/websocket/soup-websocket-connection.c
+++ b/libsoup/websocket/soup-websocket-connection.c
@@ -84,6 +84,7 @@ enum {
PROP_MAX_INCOMING_PAYLOAD_SIZE,
PROP_KEEPALIVE_INTERVAL,
PROP_EXTENSIONS,
+ PROP_MAX_TOTAL_MESSAGE_SIZE,

LAST_PROPERTY
};
@@ -126,6 +127,7 @@ typedef struct {
char *origin;
char *protocol;
guint64 max_incoming_payload_size;
+ guint64 max_total_message_size;
guint keepalive_interval;

gushort peer_close_code;
@@ -156,6 +158,7 @@ typedef struct {
} SoupWebsocketConnectionPrivate;

#define MAX_INCOMING_PAYLOAD_SIZE_DEFAULT 128 * 1024
+#define MAX_TOTAL_MESSAGE_SIZE_DEFAULT 128 * 1024
#define READ_BUFFER_SIZE 1024
#define MASK_LENGTH 4

@@ -670,7 +673,7 @@ bad_data_error_and_close (SoupWebsocketConnection *self)
}

static void
-too_big_error_and_close (SoupWebsocketConnection *self,
+too_big_incoming_payload_error_and_close (SoupWebsocketConnection *self,
guint64 payload_len)
{
SoupWebsocketConnectionPrivate *priv = soup_websocket_connection_get_instance_private (self);
@@ -687,6 +690,24 @@ too_big_error_and_close (SoupWebsocketConnection *self,
emit_error_and_close (self, error, TRUE);
}

+static void
+too_big_message_error_and_close (SoupWebsocketConnection *self,
+ guint64 len)
+{
+ SoupWebsocketConnectionPrivate *priv = soup_websocket_connection_get_instance_private (self);
+ GError *error;
+
+ error = g_error_new_literal (SOUP_WEBSOCKET_ERROR,
+ SOUP_WEBSOCKET_CLOSE_TOO_BIG,
+ priv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ?
+ "Received WebSocket payload from the client larger than configured max-total-message-size" :
+ "Received WebSocket payload from the server larger than configured max-total-message-size");
+ g_debug ("%s received message of size %" G_GUINT64_FORMAT " or greater, but max supported size is %" G_GUINT64_FORMAT,
+ priv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ? "server" : "client",
+ len, priv->max_total_message_size);
+ emit_error_and_close (self, error, TRUE);
+}
+
static void
close_connection (SoupWebsocketConnection *self,
gushort code,
@@ -918,6 +939,12 @@ process_contents (SoupWebsocketConnection *self,
switch (priv->message_opcode) {
case 0x01:
case 0x02:
+ /* Safety valve */
+ if (priv->max_total_message_size > 0 &&
+ (priv->message_data->len + payload_len) > priv->max_total_message_size) {
+ too_big_message_error_and_close (self, (priv->message_data->len + payload_len));
+ return;
+ }
g_byte_array_append (priv->message_data, payload, payload_len);
break;
default:
@@ -1056,7 +1083,7 @@ process_frame (SoupWebsocketConnection *self)
/* Safety valve */
if (priv->max_incoming_payload_size > 0 &&
payload_len > priv->max_incoming_payload_size) {
- too_big_error_and_close (self, payload_len);
+ too_big_incoming_payload_error_and_close (self, payload_len);
return FALSE;
}

@@ -1359,6 +1386,10 @@ soup_websocket_connection_get_property (GObject *object,
g_value_set_uint (value, priv->keepalive_interval);
break;

+ case PROP_MAX_TOTAL_MESSAGE_SIZE:
+ g_value_set_uint64 (value, priv->max_total_message_size);
+ break;
+
case PROP_EXTENSIONS:
g_value_set_pointer (value, priv->extensions);
break;
@@ -1384,6 +1415,10 @@ soup_websocket_connection_set_property (GObject *object,
priv->io_stream = g_value_dup_object (value);
break;

+ case PROP_MAX_TOTAL_MESSAGE_SIZE:
+ priv->max_total_message_size = g_value_get_uint64 (value);
+ break;
+
case PROP_CONNECTION_TYPE:
priv->connection_type = g_value_get_enum (value);
break;
@@ -1471,6 +1506,26 @@ soup_websocket_connection_finalize (GObject *object)
static void
soup_websocket_connection_class_init (SoupWebsocketConnectionClass *klass)
{
+ /**
+ * SoupWebsocketConnection:max-total-message-size:
+ *
+ * The total message size for incoming packets.
+ *
+ * The protocol expects or 0 to not limit it.
+ *
+ * Since: 3.8
+ */
+ properties[PROP_MAX_TOTAL_MESSAGE_SIZE] =
+ g_param_spec_uint64 ("max-total-message-size",
+ "Max total message size",
+ "Max total message size ",
+ 0,
+ G_MAXUINT64,
+ MAX_TOTAL_MESSAGE_SIZE_DEFAULT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS);
+
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

gobject_class->constructed = soup_websocket_connection_constructed;
@@ -2080,6 +2135,54 @@ soup_websocket_connection_close (SoupWebsocketConnection *self,
*/
guint64
soup_websocket_connection_get_max_incoming_payload_size (SoupWebsocketConnection *self)
+
+/**
+ * soup_websocket_connection_get_max_total_message_size:
+ * @self: the WebSocket
+ *
+ * Gets the maximum total message size allowed for packets.
+ *
+ * Returns: the maximum total message size.
+ *
+ * Since: 3.8
+ */
+SOUP_AVAILABLE_IN_ALL
+guint64
+soup_websocket_connection_get_max_total_message_size (SoupWebsocketConnection *self)
+{
+ SoupWebsocketConnectionPrivate *priv = soup_websocket_connection_get_instance_private (self);
+
+ g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), MAX_TOTAL_MESSAGE_SIZE_DEFAULT);
+
+ return priv->max_total_message_size;
+}
+
+/**
+ * soup_websocket_connection_set_max_total_message_size:
+ * @self: the WebSocket
+ * @max_total_message_size: the maximum total message size
+ *
+ * Sets the maximum total message size allowed for packets.
+ *
+ * It does not limit the outgoing packet size.
+ *
+ * Since: 3.8
+ */
+SOUP_AVAILABLE_IN_ALL
+void
+soup_websocket_connection_set_max_total_message_size (SoupWebsocketConnection *self,
+ guint64 max_total_message_size)
+{
+ SoupWebsocketConnectionPrivate *priv = soup_websocket_connection_get_instance_private (self);
+
+ g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
+
+ if (priv->max_total_message_size != max_total_message_size) {
+ priv->max_total_message_size = max_total_message_size;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MAX_TOTAL_MESSAGE_SIZE]);
+ }
+}
+
{
SoupWebsocketConnectionPrivate *priv = soup_websocket_connection_get_instance_private (self);

diff --git a/libsoup/websocket/soup-websocket-connection.h b/libsoup/websocket/soup-websocket-connection.h
index eeb093d..666756f 100644
--- a/libsoup/websocket/soup-websocket-connection.h
+++ b/libsoup/websocket/soup-websocket-connection.h
@@ -86,6 +86,14 @@ guint64 soup_websocket_connection_get_max_incoming_payload_size (Sou

SOUP_AVAILABLE_IN_ALL
void soup_websocket_connection_set_max_incoming_payload_size (SoupWebsocketConnection *self,
+
+SOUP_AVAILABLE_IN_ALL
+guint64 soup_websocket_connection_get_max_total_message_size (SoupWebsocketConnection *self);
+
+SOUP_AVAILABLE_IN_ALL
+void soup_websocket_connection_set_max_total_message_size (SoupWebsocketConnection *self,
+ guint64 max_total_message_size);
+
guint64 max_incoming_payload_size);

SOUP_AVAILABLE_IN_ALL
diff --git a/tests/websocket-test.c b/tests/websocket-test.c
index a0b8334..7d057f0 100644
--- a/tests/websocket-test.c
+++ b/tests/websocket-test.c
@@ -563,14 +563,14 @@ 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_true (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_true (soup_websocket_connection_get_max_incoming_payload_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_assert_true (g_bytes_equal (sent, received));
g_bytes_unref (sent);
g_bytes_unref (received);
}
--
2.45.4

Loading