From aa189f8bf0593427c67e0becb13f60f2da2fea26 Mon Sep 17 00:00:00 2001
From: Michael Catanzaro <mcatanzaro@redhat.com>
Date: Thu, 8 May 2025 16:16:25 -0500
Subject: [PATCH 3/4] Set message size limit in SoupServer rather than
 SoupWebsocketConnection

We're not sure about the compatibility implications of having a default
size limit for clients.

Also not sure whether the server limit is actually set appropriately,
but there is probably very little server usage of
SoupWebsocketConnection in the wild, so it's not so likely to break
things.

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

Signed-off-by: Changqing Li <changqing.li@windriver.com>
---
 libsoup/server/soup-server.c                  | 24 +++++++++++++++----
 libsoup/websocket/soup-websocket-connection.c | 24 +++++++++++++------
 2 files changed, 36 insertions(+), 12 deletions(-)

diff --git a/libsoup/server/soup-server.c b/libsoup/server/soup-server.c
index 63af0cf..023abed 100644
--- a/libsoup/server/soup-server.c
+++ b/libsoup/server/soup-server.c
@@ -188,6 +188,16 @@ static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
 
 G_DEFINE_TYPE_WITH_PRIVATE (SoupServer, soup_server, G_TYPE_OBJECT)
 
+/* SoupWebsocketConnection by default limits only maximum packet size. But a
+ * message may consist of multiple packets, so SoupServer additionally restricts
+ * total message size to mitigate denial of service attacks on the server.
+ * SoupWebsocketConnection does not do this by default because I don't know
+ * whether that would or would not cause compatibility problems for websites.
+ *
+ * This size is in bytes and it is arbitrary.
+ */
+#define MAX_TOTAL_MESSAGE_SIZE_DEFAULT   128 * 1024
+
 static void request_finished (SoupServerMessage      *msg,
                               SoupMessageIOCompletion completion,
                               SoupServer             *server);
@@ -952,11 +962,15 @@ complete_websocket_upgrade (SoupServer        *server,
 
 	g_object_ref (msg);
 	stream = soup_server_message_steal_connection (msg);
-	conn = soup_websocket_connection_new (stream, uri,
-					      SOUP_WEBSOCKET_CONNECTION_SERVER,
-					      soup_message_headers_get_one_common (soup_server_message_get_request_headers (msg), SOUP_HEADER_ORIGIN),
-					      soup_message_headers_get_one_common (soup_server_message_get_response_headers (msg), SOUP_HEADER_SEC_WEBSOCKET_PROTOCOL),
-					      handler->websocket_extensions);
+	conn = SOUP_WEBSOCKET_CONNECTION (g_object_new (SOUP_TYPE_WEBSOCKET_CONNECTION,
+					  "io-stream", stream,
+					  "uri", uri,
+					  "connection-type", SOUP_WEBSOCKET_CONNECTION_SERVER,
+					  "origin", soup_message_headers_get_one_common (soup_server_message_get_request_headers (msg), SOUP_HEADER_ORIGIN),
+					  "protocol", soup_message_headers_get_one_common (soup_server_message_get_response_headers (msg), SOUP_HEADER_SEC_WEBSOCKET_PROTOCOL),
+					  "extensions", handler->websocket_extensions,
+					  "max-total-message-size", (guint64)MAX_TOTAL_MESSAGE_SIZE_DEFAULT,
+					  NULL));
 	handler->websocket_extensions = NULL;
 	g_object_unref (stream);
 
diff --git a/libsoup/websocket/soup-websocket-connection.c b/libsoup/websocket/soup-websocket-connection.c
index a4fc36e..f60297c 100644
--- a/libsoup/websocket/soup-websocket-connection.c
+++ b/libsoup/websocket/soup-websocket-connection.c
@@ -166,7 +166,6 @@ 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
 
@@ -1681,9 +1680,10 @@ soup_websocket_connection_class_init (SoupWebsocketConnectionClass *klass)
 	/**
 	 * SoupWebsocketConnection:max-incoming-payload-size:
 	 *
-	 * The maximum payload size for incoming packets.
+	 * The maximum payload size for incoming packets, or 0 to not limit it.
 	 *
-	 * The protocol expects or 0 to not limit it.
+	 * Each message may consist of multiple packets, so also refer to
+	 * [property@WebSocketConnection:max-total-message-size].
 	 */
         properties[PROP_MAX_INCOMING_PAYLOAD_SIZE] =
                 g_param_spec_uint64 ("max-incoming-payload-size",
@@ -1754,9 +1754,19 @@ soup_websocket_connection_class_init (SoupWebsocketConnectionClass *klass)
 	/**
 	 * SoupWebsocketConnection:max-total-message-size:
 	 *
-	 * The total message size for incoming packets.
+	 * The maximum size for incoming messages.
 	 *
-	 * The protocol expects or 0 to not limit it.
+	 * Set to a value to limit the total message size, or 0 to not
+	 * limit it.
+	 *
+	 * [method@Server.add_websocket_handler] will set this to a nonzero
+	 * default value to mitigate denial of service attacks. Clients must
+	 * choose their own default if they need to mitigate denial of service
+	 * attacks. You also need to set your own default if creating your own
+	 * server SoupWebsocketConnection without using SoupServer.
+	 *
+	 * Each message may consist of multiple packets, so also refer to
+	 * [property@WebSocketConnection:max-incoming-payload-size].
 	 *
 	 * Since: 3.8
 	 */
@@ -1766,7 +1776,7 @@ soup_websocket_connection_class_init (SoupWebsocketConnectionClass *klass)
                                      "Max total message size ",
                                      0,
                                      G_MAXUINT64,
-                                     MAX_TOTAL_MESSAGE_SIZE_DEFAULT,
+                                     0,
                                      G_PARAM_READWRITE |
                                      G_PARAM_CONSTRUCT |
                                      G_PARAM_STATIC_STRINGS);
@@ -2256,7 +2266,7 @@ soup_websocket_connection_get_max_total_message_size (SoupWebsocketConnection *s
 {
 	SoupWebsocketConnectionPrivate *priv = soup_websocket_connection_get_instance_private (self);
 
-	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), MAX_TOTAL_MESSAGE_SIZE_DEFAULT);
+	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), 0);
 
 	return priv->max_total_message_size;
 }
-- 
2.34.1

