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
213 changes: 213 additions & 0 deletions SPECS/libsoup/CVE-2026-2708.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
From d9d8d64f8002ce40742d95447c0c5fbe9dac76af Mon Sep 17 00:00:00 2001
From: AllSpark <allspark@microsoft.com>
Date: Wed, 29 Apr 2026 18:28:19 +0000
Subject: [PATCH] Do not allow adding multiple content length values to headers

Closes #500

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: AI Backport of https://gitlab.gnome.org/GNOME/libsoup/-/commit/e032d3e9b0a27d10597398023532dd8f9b6654cf.patch
---
libsoup/soup-message-headers.c | 27 +++++++++++++++
tests/header-parsing-test.c | 51 +++++++++++++++++++++++++++
tests/server-test.c | 63 ++++++++++++++++++++++++++++++++++
3 files changed, 141 insertions(+)

diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
index 41d56b8..236f64d 100644
--- a/libsoup/soup-message-headers.c
+++ b/libsoup/soup-message-headers.c
@@ -263,6 +263,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 && !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 838baa6..1643a5c 100644
--- a/tests/header-parsing-test.c
+++ b/tests/header-parsing-test.c
@@ -359,6 +359,23 @@ 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 ***/
/************************/
@@ -448,6 +465,15 @@ static struct RequestTest {
{ { NULL } }, 0
},

+ { "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
+ },
+
{ "Duplicate Host headers",
"https://gitlab.gnome.org/GNOME/libsoup/-/issues/472",
"GET / HTTP/1.1\r\nHost: example.com\r\nHost: example.org\r\n",
@@ -1373,6 +1399,29 @@ do_bad_header_tests (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);
+}
+
static void
do_append_duplicate_host_test (void)
{
@@ -1415,6 +1464,8 @@ 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 96fb428..6a8bd78 100644
--- a/tests/server-test.c
+++ b/tests/server-test.c
@@ -1420,6 +1420,69 @@ do_chunked_test (ServerData *sd, gconstpointer test_data)
g_object_unref (conn);
g_object_unref (client);
}
+
+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
--
2.45.4

6 changes: 5 additions & 1 deletion SPECS/libsoup/libsoup.spec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Summary: libsoup HTTP client/server library
Name: libsoup
Version: 3.4.4
Release: 15%{?dist}
Release: 16%{?dist}
License: GPLv2
Vendor: Microsoft Corporation
Distribution: Azure Linux
Expand Down Expand Up @@ -79,6 +79,7 @@ Patch29: CVE-2026-0716.patch
Patch30: CVE-2026-2443.patch
Patch31: CVE-2026-2369.patch
Patch32: CVE-2026-2436.patch
Patch33: CVE-2026-2708.patch

%description
libsoup is HTTP client/server library for GNOME
Expand Down Expand Up @@ -154,6 +155,9 @@ find %{buildroot} -type f -name "*.la" -delete -print
%defattr(-,root,root)

%changelog
* Wed Apr 29 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 3.4.4-16
- Patch for CVE-2026-2708

* Thu Apr 02 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 3.4.4-15
- Patch for CVE-2026-2436

Expand Down
Loading