From 0fca37e0fce479284e62091ffb9b7d6caff1c7e4 Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <cgarcia@igalia.com>
Date: Thu, 29 Jan 2026 16:43:28 +0100
Subject: [PATCH] server: close the connection after responsing a request
 containing Content-Length and Transfer-Encoding

Closes #475

Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/libsoup/-/commit/6224df5a471e9040a99dd3dc2e91817a701b1bf6]
CVE: CVE-2026-1760

Signed-off-by: Changqing Li <changqing.li@windriver.com>
---
 libsoup/soup-message-headers.c   | 86 +++++++++++++++-----------------
 libsoup/soup-message-server-io.c |  8 +++
 2 files changed, 49 insertions(+), 45 deletions(-)

diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
index 535cf14..06d9600 100644
--- a/libsoup/soup-message-headers.c
+++ b/libsoup/soup-message-headers.c
@@ -666,38 +666,13 @@ clear_special_headers (SoupMessageHeaders *hdrs)
 static void
 transfer_encoding_setter (SoupMessageHeaders *hdrs, const char *value)
 {
-	if (value) {
-		/* "identity" is a wrong value according to RFC errata 408,
-		 * and RFC 7230 does not list it as valid transfer-coding.
-		 * Nevertheless, the obsolete RFC 2616 stated "identity"
-		 * as valid, so we can't handle it as unrecognized here
-		 * for compatibility reasons.
-		 */
-		if (g_ascii_strcasecmp (value, "chunked") == 0)
-			hdrs->encoding = SOUP_ENCODING_CHUNKED;
-		else if (g_ascii_strcasecmp (value, "identity") != 0)
-			hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
-	} else
-		hdrs->encoding = -1;
+	hdrs->encoding = -1;
 }
 
 static void
 content_length_setter (SoupMessageHeaders *hdrs, const char *value)
 {
-	/* Transfer-Encoding trumps Content-Length */
-	if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
-		return;
-
-	if (value) {
-		char *end;
-
-		hdrs->content_length = g_ascii_strtoull (value, &end, 10);
-		if (*end)
-			hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
-		else
-			hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
-	} else
-		hdrs->encoding = -1;
+	hdrs->encoding = -1;
 }
 
 /**
@@ -730,29 +705,50 @@ SoupEncoding
 soup_message_headers_get_encoding (SoupMessageHeaders *hdrs)
 {
 	const char *header;
+	const char *content_length;
+	const char *transfer_encoding;
 
 	if (hdrs->encoding != -1)
 		return hdrs->encoding;
 
-	/* If Transfer-Encoding was set, hdrs->encoding would already
-	 * be set. So we don't need to check that possibility.
-	 */
-	header = soup_message_headers_get_one (hdrs, "Content-Length");
-	if (header) {
-		content_length_setter (hdrs, header);
-		if (hdrs->encoding != -1)
-			return hdrs->encoding;
-	}
+			/* Transfer-Encoding is checked first because it overrides the Content-Length */
+			transfer_encoding = soup_message_headers_get_one (hdrs, "Transfer-Encoding");
+			if (transfer_encoding) {
+					/* "identity" is a wrong value according to RFC errata 408,
+					 * and RFC 7230 does not list it as valid transfer-coding.
+					 * Nevertheless, the obsolete RFC 2616 stated "identity"
+					 * as valid, so we can't handle it as unrecognized here
+					 * for compatibility reasons.
+					 */
+					if (g_ascii_strcasecmp (transfer_encoding, "chunked") == 0)
+							hdrs->encoding = SOUP_ENCODING_CHUNKED;
+					else if (g_ascii_strcasecmp (transfer_encoding, "identity") != 0)
+							hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
+			} else {
+					content_length = soup_message_headers_get_one (hdrs, "Content-Length");
+					if (content_length) {
+							char *end;
+
+							hdrs->content_length = g_ascii_strtoull (content_length, &end, 10);
+							if (*end)
+									hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
+							else
+									hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
+					}
+			}
+
+			if (hdrs->encoding == -1) {
+					/* Per RFC 2616 4.4, a response body that doesn't indicate its
+					 * encoding otherwise is terminated by connection close, and a
+					 * request that doesn't indicate otherwise has no body. Note
+					 * that SoupMessage calls soup_message_headers_set_encoding()
+					 * to override the response body default for our own
+					 * server-side messages.
+					 */
+					hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
+							SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
+			}
 
-	/* Per RFC 2616 4.4, a response body that doesn't indicate its
-	 * encoding otherwise is terminated by connection close, and a
-	 * request that doesn't indicate otherwise has no body. Note
-	 * that SoupMessage calls soup_message_headers_set_encoding()
-	 * to override the response body default for our own
-	 * server-side messages.
-	 */
-	hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
-		SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
 	return hdrs->encoding;
 }
 
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index 71e943b..df5eafc 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -80,6 +80,14 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
 			return SOUP_STATUS_BAD_REQUEST;
 	}
 
+	/* A server MAY reject a request that contains both Content-Length and
+	 * Transfer-Encoding or process such a request in accordance with the
+	 * Transfer-Encoding alone. Regardless, the server MUST close the connection
+	 * after responding to such a request to avoid the potential attacks
+	 */
+	if (*encoding == SOUP_ENCODING_CHUNKED && soup_message_headers_get_one (msg->request_headers, "Content-Length"))
+			soup_message_headers_replace (msg->request_headers, "Connection", "close");
+
 	/* Generate correct context for request */
 	req_host = soup_message_headers_get_one (msg->request_headers, "Host");
 	if (req_host && strchr (req_host, '/')) {
-- 
2.34.1

