From 25616e1a958bc1503cc24d6845a6e80ffc287727 Mon Sep 17 00:00:00 2001
From: Michael Catanzaro <mcatanzaro@redhat.com>
Date: Thu, 8 May 2025 16:16:25 -0500
Subject: [PATCH] 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 | 26 +++++++++++++------
 2 files changed, 37 insertions(+), 13 deletions(-)

diff --git a/libsoup/server/soup-server.c b/libsoup/server/soup-server.c
index 6b486f5..c779f7d 100644
--- a/libsoup/server/soup-server.c
+++ b/libsoup/server/soup-server.c
@@ -186,6 +186,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);
@@ -937,11 +947,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 26476df..cbb1b72 100644
--- a/libsoup/websocket/soup-websocket-connection.c
+++ b/libsoup/websocket/soup-websocket-connection.c
@@ -149,7 +149,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
 
@@ -1612,9 +1611,10 @@ soup_websocket_connection_class_init (SoupWebsocketConnectionClass *klass)
 	/**
 	 * SoupWebsocketConnection:max-incoming-payload-size:
 	 *
-	 * The maximum payload size for incoming packets.
-	 *
-	 * The protocol expects or 0 to not limit it.
+     * The maximum payload size for incoming packets, 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",
@@ -1662,9 +1662,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.
+	 *
+	 * 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.
 	 *
-	 * The protocol expects or 0 to not limit it.
+	 * Each message may consist of multiple packets, so also refer to
+	 * [property@WebSocketConnection:max-incoming-payload-size].
 	 *
 	 * Since: 3.8
 	 */
@@ -1674,7 +1684,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);
@@ -2164,7 +2174,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

