diff mbox series

libsoup: patch CVE-2026-2708

Message ID 20260510092621.174841-1-peter.marko@siemens.com
State New
Headers show
Series libsoup: patch CVE-2026-2708 | expand

Commit Message

Peter Marko May 10, 2026, 9:26 a.m. UTC
Pick patch per [1].

[1] https://security-tracker.debian.org/tracker/CVE-2026-2708

Signed-off-by: Peter Marko <peter.marko@siemens.com>
---
 .../libsoup/libsoup/CVE-2026-2708.patch       | 218 ++++++++++++++++++
 meta/recipes-support/libsoup/libsoup_3.6.6.bb |   1 +
 2 files changed, 219 insertions(+)
 create mode 100644 meta/recipes-support/libsoup/libsoup/CVE-2026-2708.patch
diff mbox series

Patch

diff --git a/meta/recipes-support/libsoup/libsoup/CVE-2026-2708.patch b/meta/recipes-support/libsoup/libsoup/CVE-2026-2708.patch
new file mode 100644
index 0000000000..479a53ae21
--- /dev/null
+++ b/meta/recipes-support/libsoup/libsoup/CVE-2026-2708.patch
@@ -0,0 +1,218 @@ 
+From e032d3e9b0a27d10597398023532dd8f9b6654cf Mon Sep 17 00:00:00 2001
+From: Carlos Garcia Campos <cgarcia@igalia.com>
+Date: Tue, 17 Feb 2026 16:39:26 +0100
+Subject: [PATCH] Do not allow adding multiple content length values to headers
+
+Closes #500
+
+CVE: CVE-2026-2708
+Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/libsoup/-/commit/e032d3e9b0a27d10597398023532dd8f9b6654cf]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ libsoup/soup-message-headers.c | 27 ++++++++++++++
+ tests/header-parsing-test.c    | 50 +++++++++++++++++++++++++-
+ tests/server-test.c            | 64 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 140 insertions(+), 1 deletion(-)
+
+diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
+index ca7719db..34cd2d8f 100644
+--- a/libsoup/soup-message-headers.c
++++ b/libsoup/soup-message-headers.c
+@@ -269,6 +269,33 @@ soup_message_headers_append_common (SoupMessageHeaders    *hdrs,
+                 return FALSE;
+         }
+ 
++        if (name == SOUP_HEADER_CONTENT_LENGTH) {
++                /* RFC 9110 - 7.7. Content-Length
++                 * If a message is received that has a Content-Length header field value consisting of
++                 * the same decimal value as a comma-separated list (Section 5.7.1) — for example,
++                 * "Content-Length: 42, 42" — indicating that duplicate Content-Length header fields have
++                 * been generated or combined by an upstream message processor, then the recipient must either
++                 * reject the message as invalid or replace the duplicated field values with a single valid
++                 * Content-Length field containing that decimal value prior to determining the message body
++                 * length or forwarding the message.
++                 */
++                const char *content_length = soup_message_headers_get_one_common (hdrs, SOUP_HEADER_CONTENT_LENGTH);
++                if (content_length) {
++                        guint64 decimal_value1, decimal_value2;
++                        char *end;
++
++                        decimal_value1 = g_ascii_strtoull (content_length, &end, 10);
++                        if (*end)
++                                return FALSE;
++
++                        decimal_value2 = g_ascii_strtoull (value, &end, 10);
++                        if (*end)
++                                return FALSE;
++
++                        return decimal_value1 == decimal_value2;
++                }
++        }
++
+         if (trusted_value == SOUP_HEADER_VALUE_UNTRUSTED && !is_valid_header_value (value)) {
+                 g_warning ("soup_message_headers_append: Rejecting bad value '%s'", value);
+                 return FALSE;
+diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c
+index ebdc3246..5a3c92eb 100644
+--- a/tests/header-parsing-test.c
++++ b/tests/header-parsing-test.c
+@@ -368,6 +368,22 @@ static struct RequestTest {
+ 	  }, 0
+ 	},
+ 
++        { "Duplicate Content-Length with the same value", NULL,
++          "POST / HTTP/1.1\r\nContent-Length: 4\r\nContent-Length: 4\r\n",
++          -1,
++          SOUP_STATUS_OK,
++          "POST", "/", SOUP_HTTP_1_1,
++          { { "Content-Length", "4" } }, 0
++        },
++
++        { "Duplicate Content-Length with the same decimal value", NULL,
++          "POST / HTTP/1.1\r\nContent-Length: 04\r\nContent-Length: 4\r\n",
++          -1,
++          SOUP_STATUS_OK,
++          "POST", "/", SOUP_HTTP_1_1,
++          { { "Content-Length", "04" } }, 0
++        },
++
+ 	/************************/
+ 	/*** INVALID REQUESTS ***/
+ 	/************************/
+@@ -507,7 +523,16 @@ static struct RequestTest {
+ 	  NULL, NULL, -1,
+ 	  { { NULL } },
+ 	  G_LOG_LEVEL_WARNING
+-	}
++	},
++
++        { "Duplicate Content-Length with different value",
++          "https://gitlab.gnome.org/GNOME/libsoup/-/issues/500",
++          "POST / HTTP/1.1\r\nContent-Length: 2\r\nContent-Length: 4\r\n",
++          -1,
++          SOUP_STATUS_BAD_REQUEST,
++          NULL, NULL, -1,
++          { { NULL } }, 0
++        }
+ };
+ static const int num_reqtests = G_N_ELEMENTS (reqtests);
+ 
+@@ -1475,6 +1500,28 @@ do_append_duplicate_host_test (void)
+ 	soup_message_headers_unref (hdrs);
+ }
+ 
++static void
++do_append_duplicate_content_length_test (void)
++{
++        SoupMessageHeaders *hdrs;
++        const char *list_value;
++
++        hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
++        soup_message_headers_append (hdrs, "Content-Length", "42");
++
++        /* Inserting the same value doesn't generate a list */
++        soup_message_headers_append (hdrs, "Content-Length", "42");
++        list_value = soup_message_headers_get_list (hdrs, "Content-Length");
++        g_assert_cmpstr (list_value, ==, "42");
++
++        /* Inserting a different value does nothing */
++        soup_message_headers_append (hdrs, "Content-Length", "45");
++        list_value = soup_message_headers_get_list (hdrs, "Content-Length");
++        g_assert_cmpstr (list_value, ==, "42");
++
++        soup_message_headers_unref (hdrs);
++}
++
+ int
+ main (int argc, char **argv)
+ {
+@@ -1491,6 +1538,7 @@ main (int argc, char **argv)
+ 	g_test_add_func ("/header-parsing/append-param", do_append_param_tests);
+ 	g_test_add_func ("/header-parsing/bad", do_bad_header_tests);
+ 	g_test_add_func ("/header-parsing/append-duplicate-host", do_append_duplicate_host_test);
++        g_test_add_func ("/header-parsing/append-duplicate-content-length", do_append_duplicate_content_length_test);
+ 
+ 	ret = g_test_run ();
+ 
+diff --git a/tests/server-test.c b/tests/server-test.c
+index 09ea03b3..59230444 100644
+--- a/tests/server-test.c
++++ b/tests/server-test.c
+@@ -1422,6 +1422,68 @@ do_chunked_test (ServerData *sd, gconstpointer test_data)
+         }
+ }
+ 
++static void
++do_multiple_content_length_test (ServerData *sd, gconstpointer test_data)
++{
++        gint i;
++        struct {
++                const char *description;
++                const char *test;
++                const char *expected_response;
++        } tests[] = {
++                { "Double Content-Length with different value", "POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 0\r\nContent-Length: 4\r\nConnection: close\r\n\r\n\r\nABCD", "HTTP/1.0 400 Bad Request" },
++                { "Double Content-Length with the same value", "POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 4\r\nContent-Length: 4\r\nConnection: close\r\n\r\n\r\nABCD", "HTTP/1.1 200 OK" },
++        };
++
++        sd->server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
++        sd->base_uri = soup_test_server_get_uri (sd->server, "http", NULL);
++        server_add_handler (sd, NULL, server_callback, NULL, NULL);
++
++        for (i = 0; i < G_N_ELEMENTS (tests); i++) {
++                GSocketClient *client;
++                GSocketConnection *conn;
++                GInputStream *input;
++                GOutputStream *output;
++                gsize nwritten;
++                char buffer[4096];
++                gssize nread;
++                GString *response;
++                const char *boundary;
++                GError *error = NULL;
++
++                debug_printf (1, "  %s\n", tests[i].description);
++
++                client = g_socket_client_new ();
++                conn = g_socket_client_connect_to_host (client, g_uri_get_host (sd->base_uri), g_uri_get_port (sd->base_uri), NULL, &error);
++                g_assert_no_error (error);
++
++                output = g_io_stream_get_output_stream (G_IO_STREAM (conn));
++                g_output_stream_write_all (output, tests[i].test, strlen (tests[i].test), &nwritten, NULL, &error);
++                g_assert_no_error (error);
++                g_assert_cmpuint (nwritten, ==, strlen (tests[i].test));
++                g_output_stream_flush (output, NULL, &error);
++                g_assert_no_error (error);
++
++                response = g_string_new (NULL);
++
++                input = g_io_stream_get_input_stream (G_IO_STREAM (conn));
++                do {
++                        nread = g_input_stream_read (input, buffer, sizeof(buffer), NULL, NULL);
++                        if (nread >= 0)
++                                response = g_string_append_len (response, (const char *)buffer, nread);
++                } while (nread > 0);
++
++                boundary = strstr (response->str, "\r\n");
++                g_assert_nonnull (boundary);
++                response = g_string_truncate (response, response->len - strlen (boundary));
++                g_assert_cmpstr (response->str, ==, tests[i].expected_response);
++                g_string_free (response, TRUE);
++
++                g_object_unref (conn);
++                g_object_unref (client);
++        }
++}
++
+ int
+ main (int argc, char **argv)
+ {
+@@ -1464,6 +1526,8 @@ main (int argc, char **argv)
+ 		    server_setup, do_steal_connect_test, server_teardown);
+         g_test_add ("/server/chunked", ServerData, NULL,
+                     NULL, do_chunked_test, server_teardown);
++        g_test_add ("/server/multiple-content-length", ServerData, NULL,
++                    NULL, do_multiple_content_length_test, server_teardown);
+ 
+ 	ret = g_test_run ();
+ 
diff --git a/meta/recipes-support/libsoup/libsoup_3.6.6.bb b/meta/recipes-support/libsoup/libsoup_3.6.6.bb
index 792cb26e93..d18a16379b 100644
--- a/meta/recipes-support/libsoup/libsoup_3.6.6.bb
+++ b/meta/recipes-support/libsoup/libsoup_3.6.6.bb
@@ -19,6 +19,7 @@  SRC_URI += "file://CVE-2025-32049-1.patch \
             file://CVE-2025-32049-4.patch \
             file://CVE-2026-1539.patch \
             file://CVE-2026-5119.patch \
+            file://CVE-2026-2708.patch \
 "
 
 PROVIDES = "libsoup-3.0"