From 2a0a7eca14789ff4cb2325af5596e6425d89d849 Mon Sep 17 00:00:00 2001 From: Azure Linux Security Servicing Account Date: Mon, 9 Feb 2026 18:22:20 +0000 Subject: [PATCH] Patch libsoup for CVE-2026-1801, CVE-2026-1761, CVE-2026-1467 --- SPECS/libsoup/CVE-2026-1467.patch | 222 +++++++++++++++++++++ SPECS/libsoup/CVE-2026-1761.patch | 100 ++++++++++ SPECS/libsoup/CVE-2026-1801.patch | 312 ++++++++++++++++++++++++++++++ SPECS/libsoup/libsoup.spec | 8 +- 4 files changed, 641 insertions(+), 1 deletion(-) create mode 100644 SPECS/libsoup/CVE-2026-1467.patch create mode 100644 SPECS/libsoup/CVE-2026-1761.patch create mode 100644 SPECS/libsoup/CVE-2026-1801.patch diff --git a/SPECS/libsoup/CVE-2026-1467.patch b/SPECS/libsoup/CVE-2026-1467.patch new file mode 100644 index 00000000000..199402ba858 --- /dev/null +++ b/SPECS/libsoup/CVE-2026-1467.patch @@ -0,0 +1,222 @@ +From 437f2dad08935d526038ee919424f71701afa590 Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Mon, 9 Feb 2026 17:54:15 +0000 +Subject: [PATCH] uri-utils: do host validation when checking if a GUri is + valid + +Currently we only check if the host is not NULL and not empty, but it might contain invalid characters not allowed for a host name in a URL. This change replaces the SOUP_URI_IS_VALID internal macro by a function that in addition to the existing checks, also validates the host.\n\nCloses #488 + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/498.patch +--- + libsoup/auth/soup-auth.c | 2 +- + libsoup/soup-message.c | 6 ++-- + libsoup/soup-uri-utils-private.h | 4 +-- + libsoup/soup-uri-utils.c | 61 ++++++++++++++++++++++++++++++++ + tests/uri-parsing-test.c | 47 ++++++++++++++++++++++++ + 5 files changed, 114 insertions(+), 6 deletions(-) + +diff --git a/libsoup/auth/soup-auth.c b/libsoup/auth/soup-auth.c +index d9bf4af..278baa1 100644 +--- a/libsoup/auth/soup-auth.c ++++ b/libsoup/auth/soup-auth.c +@@ -643,7 +643,7 @@ GSList * + soup_auth_get_protection_space (SoupAuth *auth, GUri *source_uri) + { + g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL); +- g_return_val_if_fail (SOUP_URI_IS_VALID (source_uri), NULL); ++ g_return_val_if_fail (soup_uri_is_valid (source_uri), NULL); + + GUri *source_uri_normalized = soup_uri_copy_with_normalized_flags (source_uri); + GSList *ret = SOUP_AUTH_GET_CLASS (auth)->get_protection_space (auth, source_uri_normalized); +diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c +index 2f5b267..3bbac1b 100644 +--- a/libsoup/soup-message.c ++++ b/libsoup/soup-message.c +@@ -946,7 +946,7 @@ SoupMessage * + soup_message_new_from_uri (const char *method, GUri *uri) + { + g_return_val_if_fail (method != NULL, NULL); +- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL); ++ g_return_val_if_fail (soup_uri_is_valid (uri), NULL); + + return g_object_new (SOUP_TYPE_MESSAGE, + "method", method, +@@ -966,7 +966,7 @@ soup_message_new_from_uri (const char *method, GUri *uri) + SoupMessage * + soup_message_new_options_ping (GUri *base_uri) + { +- g_return_val_if_fail (SOUP_URI_IS_VALID (base_uri), NULL); ++ g_return_val_if_fail (soup_uri_is_valid (base_uri), NULL); + + return g_object_new (SOUP_TYPE_MESSAGE, + "method", SOUP_METHOD_OPTIONS, +@@ -2039,7 +2039,7 @@ soup_message_set_uri (SoupMessage *msg, GUri *uri) + GUri *normalized_uri; + + g_return_if_fail (SOUP_IS_MESSAGE (msg)); +- g_return_if_fail (SOUP_URI_IS_VALID (uri)); ++ g_return_if_fail (soup_uri_is_valid (uri)); + + priv = soup_message_get_instance_private (msg); + +diff --git a/libsoup/soup-uri-utils-private.h b/libsoup/soup-uri-utils-private.h +index 3dbdb85..a73e882 100644 +--- a/libsoup/soup-uri-utils-private.h ++++ b/libsoup/soup-uri-utils-private.h +@@ -10,6 +10,8 @@ + + G_BEGIN_DECLS + ++gboolean soup_uri_is_valid (GUri *uri); ++ + gboolean soup_uri_is_http (GUri *uri); + + gboolean soup_uri_is_https (GUri *uri); +@@ -28,6 +30,4 @@ GUri *soup_uri_copy_with_normalized_flags (GUri *uri); + + char *soup_uri_get_host_for_headers (GUri *uri); + +-#define SOUP_URI_IS_VALID(x) (x && g_uri_get_host(x) && g_uri_get_host(x)[0]) +- + G_END_DECLS +diff --git a/libsoup/soup-uri-utils.c b/libsoup/soup-uri-utils.c +index ce9b2a1..3289798 100644 +--- a/libsoup/soup-uri-utils.c ++++ b/libsoup/soup-uri-utils.c +@@ -244,6 +244,67 @@ soup_uri_host_equal (gconstpointer v1, gconstpointer v2) + return g_ascii_strcasecmp (one_host, two_host) == 0; + } + ++static gboolean ++is_valid_character_for_host (char c) ++{ ++ static const char forbidden_chars[] = { ' ', '\n', '\r', ' ', '#', '/', ':', '<', '>', '?', '@', '[', '\\', ']', '^', '|' }; ++ int i; ++ ++ for (i = 0; i < G_N_ELEMENTS (forbidden_chars); ++i) { ++ if (c == forbidden_chars[i]) ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++is_host_valid (const char* host) ++{ ++ int i; ++ gboolean is_valid; ++ char *ascii_host = NULL; ++ ++ if (!host || !host[0]) ++ return FALSE; ++ ++ if (g_hostname_is_non_ascii (host)) { ++ ascii_host = g_hostname_to_ascii (host); ++ if (!ascii_host) ++ return FALSE; ++ ++ host = ascii_host; ++ } ++ ++ if ((g_ascii_isdigit (host[0]) || strchr (host, ':')) && g_hostname_is_ip_address (host)) { ++ g_free (ascii_host); ++ return TRUE; ++ } ++ ++ is_valid = TRUE; ++ for (i = 0; host[i] && is_valid; i++) ++ is_valid = is_valid_character_for_host (host[i]); ++ ++ g_free (ascii_host); ++ ++ return is_valid; ++} ++ ++gboolean ++soup_uri_is_valid (GUri *uri) ++{ ++ if (!uri) ++ return FALSE; ++ ++ if (!is_host_valid (g_uri_get_host (uri))) ++ return FALSE; ++ ++ /* FIXME: validate other URI components? */ ++ ++ return TRUE; ++} ++ ++ + gboolean + soup_uri_is_https (GUri *uri) + { +diff --git a/tests/uri-parsing-test.c b/tests/uri-parsing-test.c +index 4c16d7e..27e6439 100644 +--- a/tests/uri-parsing-test.c ++++ b/tests/uri-parsing-test.c +@@ -116,6 +116,52 @@ do_copy_tests (void) + g_uri_unref (uri); + } + ++ ++static struct { ++ const char *scheme; ++ const char *host; ++ const char *as_string; ++ gboolean valid; ++} valid_tests[] = { ++ { "http", "example.com", "http://example.com/", TRUE }, ++ { "http", "localhost", "http://localhost/", TRUE }, ++ { "http", "127.0.0.1", "http://127.0.0.1/", TRUE }, ++ { "http", "::1", "http://[::1]/", TRUE }, ++ { "http", "::192.168.0.10", "http://[::192.168.0.10]/", TRUE }, ++ { "http", "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/", TRUE }, ++ { "http", "\xe4\xbe\x8b\xe5\xad\x90.\xe6\xb5\x8b\xe8\xaf\x95", "http://\xe4\xbe\x8b\xe5\xad\x90.\xe6\xb5\x8b\xe8\xaf\x95/", TRUE }, ++ { "http", "012x:4567:89AB:cdef:3210:7654:ba98:FeDc", "http://012x:4567:89AB:cdef:3210:7654:ba98:FeDc/", FALSE }, ++ { "http", " example.com", "http:// example.com/", FALSE }, ++ { "http", "example.com\n", "http://example.com\n/", FALSE }, ++ { "http", "\r\nexample.com", "http://\r\nexample.com/", FALSE }, ++ { "http", "example .com", "http://example .com/", FALSE }, ++ { "http", "example:com", "http://example:com/", FALSE }, ++ { "http", "exampl.com", "http://exampl.com/", FALSE }, ++ { "http", "exampl[e].com", "http://exampl[e].com/", FALSE }, ++ { "http", "exampl^e.com", "http://exampl^e.com/", FALSE }, ++ { "http", "examp|e.com", "http://examp|e.com/", FALSE }, ++}; ++ ++static void ++do_valid_tests (void) ++{ ++ int i; ++ ++ for (i = 0; i < G_N_ELEMENTS (valid_tests); ++i) { ++ GUri *uri; ++ char *uri_str; ++ ++ uri = g_uri_build (SOUP_HTTP_URI_FLAGS | G_URI_FLAGS_ENCODED, valid_tests[i].scheme, NULL, valid_tests[i].host, -1, "", NULL, NULL); ++ uri_str = g_uri_to_string (uri); ++ ++ g_assert_cmpstr (uri_str, ==, valid_tests[i].as_string); ++ g_assert_true (soup_uri_is_valid (uri) == valid_tests[i].valid); ++ ++ g_free (uri_str); ++ g_uri_unref (uri); ++ } ++} ++ + #define CONTENT_TYPE_DEFAULT "text/plain;charset=US-ASCII" + + static struct { +@@ -192,6 +238,7 @@ main (int argc, char **argv) + + g_test_add_func ("/uri/equality", do_equality_tests); + g_test_add_func ("/uri/copy", do_copy_tests); ++ g_test_add_func ("/uri/valid", do_valid_tests); + g_test_add_func ("/data", do_data_uri_tests); + g_test_add_func ("/path_and_query", do_path_and_query_tests); + +-- +2.45.4 + diff --git a/SPECS/libsoup/CVE-2026-1761.patch b/SPECS/libsoup/CVE-2026-1761.patch new file mode 100644 index 00000000000..50584204b1a --- /dev/null +++ b/SPECS/libsoup/CVE-2026-1761.patch @@ -0,0 +1,100 @@ +From 3ac5a0e9096db5ad74ebe7e5cdc63c7f11ae4e22 Mon Sep 17 00:00:00 2001 +From: Carlos Garcia Campos +Date: Mon, 19 Jan 2026 15:14:58 +0100 +Subject: [PATCH] multipart: check length of bytes read + soup_filter_input_stream_read_until() + +We do make sure the read length is smaller than the buffer length when +the boundary is not found, but we should do the same when the boundary +is found. + +Spotted in #YWH-PGM9867-149 +Closes #493 + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/496.patch +--- + libsoup/soup-filter-input-stream.c | 3 +- + tests/multipart-test.c | 46 ++++++++++++++++++++++++++++++ + 2 files changed, 48 insertions(+), 1 deletion(-) + +diff --git a/libsoup/soup-filter-input-stream.c b/libsoup/soup-filter-input-stream.c +index b1e616c..22541aa 100644 +--- a/libsoup/soup-filter-input-stream.c ++++ b/libsoup/soup-filter-input-stream.c +@@ -337,6 +337,7 @@ soup_filter_input_stream_read_until (SoupFilterInputStream *fstream, + if (eof && !*got_boundary) + read_length = MIN (priv->buf->len, length); + else +- read_length = p - buf; ++ read_length = MIN ((gsize)(p - buf), length); ++ + return read_from_buf (fstream, buffer, read_length); + } +diff --git a/tests/multipart-test.c b/tests/multipart-test.c +index d05000f..a83fc64 100644 +--- a/tests/multipart-test.c ++++ b/tests/multipart-test.c +@@ -550,6 +550,51 @@ test_multipart_bounds_bad_2 (void) + g_bytes_unref (bytes); + } + ++static void ++test_multipart_bounds_bad_3 (void) ++{ ++ SoupMessage *msg; ++ SoupMessageHeaders *headers; ++ GInputStream *in; ++ SoupMultipartInputStream *multipart; ++ GError *error = NULL; ++ const char raw_data[] = "\0$--A\r\nContent-Disposition: form-data; name=\"f\"\r\n\r\nXXXXXXXXX\r\n--A--\r\n"; ++ ++ msg = soup_message_new(SOUP_METHOD_POST, "http://foo/upload"); ++ headers = soup_message_get_response_headers (msg); ++ soup_message_headers_replace (headers, "Content-Type", "multipart/form-data; boundary=\"A\""); ++ ++ in = g_memory_input_stream_new_from_data (raw_data + 2, sizeof(raw_data) - 2, NULL); ++ multipart = soup_multipart_input_stream_new (msg, in); ++ g_object_unref (in); ++ ++ while (TRUE) { ++ in = soup_multipart_input_stream_next_part (multipart, NULL, &error); ++ g_assert_no_error (error); ++ if (!in) { ++ g_clear_error (&error); ++ break; ++ } ++ ++ char buffer[10]; ++ while (TRUE) { ++ gssize bytes_read; ++ ++ bytes_read = g_input_stream_read (in, buffer, sizeof(buffer), NULL, &error); ++ g_assert_no_error (error); ++ if (bytes_read <= 0) { ++ g_clear_error (&error); ++ break; ++ } ++ } ++ ++ g_object_unref (in); ++ } ++ ++ g_object_unref (multipart); ++ g_object_unref (msg); ++} ++ + static void + test_multipart_too_large (void) + { +@@ -619,6 +664,7 @@ main (int argc, char **argv) + g_test_add_func ("/multipart/bounds-good", test_multipart_bounds_good); + g_test_add_func ("/multipart/bounds-bad", test_multipart_bounds_bad); + g_test_add_func ("/multipart/bounds-bad-2", test_multipart_bounds_bad_2); ++ g_test_add_func ("/multipart/bounds-bad-3", test_multipart_bounds_bad_3); + g_test_add_func ("/multipart/too-large", test_multipart_too_large); + + ret = g_test_run (); +-- +2.45.4 + diff --git a/SPECS/libsoup/CVE-2026-1801.patch b/SPECS/libsoup/CVE-2026-1801.patch new file mode 100644 index 00000000000..d578e27febe --- /dev/null +++ b/SPECS/libsoup/CVE-2026-1801.patch @@ -0,0 +1,312 @@ +From 30d2e8f78bf640d05e1e70b7d6b35e8deebb9d75 Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Mon, 9 Feb 2026 18:01:57 +0000 +Subject: [PATCH] http1: Use CRLF boundary when parsing chunked encoding lines; + server: close connection on requests with both Content-Length and + Transfer-Encoding; headers: prioritize Transfer-Encoding over Content-Length. + Add tests for chunked parsing and connection closing. + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/506.patch +--- + libsoup/http1/soup-body-input-stream.c | 34 ++++++---- + libsoup/server/soup-server-io.c | 8 +++ + libsoup/soup-message-headers.c | 89 ++++++++++++-------------- + tests/server-test.c | 70 ++++++++++++++++++++ + 4 files changed, 140 insertions(+), 61 deletions(-) + +diff --git a/libsoup/http1/soup-body-input-stream.c b/libsoup/http1/soup-body-input-stream.c +index 28acae8..b85a2b8 100644 +--- a/libsoup/http1/soup-body-input-stream.c ++++ b/libsoup/http1/soup-body-input-stream.c +@@ -176,15 +176,19 @@ soup_body_input_stream_read_chunked (SoupBodyInputStream *bistream, + again: + switch (priv->chunked_state) { + case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE: +- nread = soup_filter_input_stream_read_line ( +- fstream, metabuf, sizeof (metabuf), blocking, ++ nread = soup_filter_input_stream_read_until ( ++ fstream, metabuf, sizeof (metabuf), ++ "\r\n", 2, blocking, TRUE, + &got_line, cancellable, error); +- if (nread <= 0) ++ if (nread < 0) + return nread; +- if (!got_line) { +- g_set_error_literal (error, G_IO_ERROR, +- G_IO_ERROR_PARTIAL_INPUT, +- _("Connection terminated unexpectedly")); ++ ++ if (nread == 0 || !got_line) { ++ if (error && *error == NULL) { ++ g_set_error_literal (error, G_IO_ERROR, ++ G_IO_ERROR_PARTIAL_INPUT, ++ _("Connection terminated unexpectedly")); ++ } + return -1; + } + +@@ -208,11 +212,12 @@ again: + return nread; + + case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END: +- nread = soup_filter_input_stream_read_line ( ++ nread = soup_filter_input_stream_read_until ( + SOUP_FILTER_INPUT_STREAM (priv->base_stream), +- metabuf, sizeof (metabuf), blocking, ++ metabuf, sizeof (metabuf), ++ "\r\n", 2, blocking, TRUE, + &got_line, cancellable, error); +- if (nread <= 0) ++ if (nread < 0) + return nread; + if (!got_line) { + g_set_error_literal (error, G_IO_ERROR, +@@ -225,13 +230,14 @@ again: + break; + + case SOUP_BODY_INPUT_STREAM_STATE_TRAILERS: +- nread = soup_filter_input_stream_read_line ( +- fstream, buffer, count, blocking, ++ nread = soup_filter_input_stream_read_until ( ++ fstream, buffer, count, ++ "\r\n", 2, blocking, TRUE, + &got_line, cancellable, error); +- if (nread <= 0) ++ if (nread < 0) + return nread; + +- if (strncmp (buffer, "\r\n", nread) || strncmp (buffer, "\n", nread)) { ++ if (nread == 2 && strncmp (buffer, "\r\n", nread) == 0) { + priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_DONE; + priv->eof = TRUE; + } +diff --git a/libsoup/server/soup-server-io.c b/libsoup/server/soup-server-io.c +index d1af115..47e7077 100644 +--- a/libsoup/server/soup-server-io.c ++++ b/libsoup/server/soup-server-io.c +@@ -599,6 +599,14 @@ parse_headers (SoupServerMessage *msg, + 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_common (request_headers, SOUP_HEADER_CONTENT_LENGTH)) ++ soup_message_headers_replace_common (request_headers, SOUP_HEADER_CONNECTION, "close"); ++ + /* Generate correct context for request */ + req_host = soup_message_headers_get_one_common (request_headers, SOUP_HEADER_HOST); + if (req_host && strchr (req_host, '/')) { +diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c +index 9123dc1..1128106 100644 +--- a/libsoup/soup-message-headers.c ++++ b/libsoup/soup-message-headers.c +@@ -155,19 +155,8 @@ soup_message_headers_set (SoupMessageHeaders *hdrs, + { + switch (name) { + case SOUP_HEADER_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; ++ case SOUP_HEADER_TRANSFER_ENCODING: ++ hdrs->encoding = -1; + break; + case SOUP_HEADER_CONTENT_TYPE: + g_clear_pointer (&hdrs->content_type, g_free); +@@ -193,21 +182,6 @@ soup_message_headers_set (SoupMessageHeaders *hdrs, + } else + hdrs->expectations = 0; + break; +- case SOUP_HEADER_TRANSFER_ENCODING: +- 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; +- break; + default: + break; + } +@@ -928,30 +902,51 @@ soup_message_headers_foreach (SoupMessageHeaders *hdrs, + SoupEncoding + soup_message_headers_get_encoding (SoupMessageHeaders *hdrs) + { +- const char *header; ++ const char *content_length; ++ const char *transfer_encoding; ++ ++ g_return_val_if_fail (hdrs, SOUP_ENCODING_UNRECOGNIZED); + + 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_common (hdrs, SOUP_HEADER_CONTENT_LENGTH); +- if (header) { +- soup_message_headers_set (hdrs, SOUP_HEADER_CONTENT_LENGTH, 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_common (hdrs, SOUP_HEADER_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_common (hdrs, SOUP_HEADER_CONTENT_LENGTH); ++ if (content_length) { ++ char *end; + +- /* 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; ++ 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; ++ } + return hdrs->encoding; + } + +diff --git a/tests/server-test.c b/tests/server-test.c +index 2f9c499..abf2ab8 100644 +--- a/tests/server-test.c ++++ b/tests/server-test.c +@@ -1292,6 +1292,73 @@ do_steal_connect_test (ServerData *sd, gconstpointer test_data) + g_uri_unref (proxy_uri); + g_free (proxy_uri_str); + } ++static void ++server_chunked_hundler (SoupServer *server, ++ SoupServerMessage *msg, ++ const char *path, ++ GHashTable *query, ++ gpointer data) ++{ ++ g_assert_true (soup_server_message_get_method (msg) == SOUP_METHOD_POST); ++ g_assert_cmpstr (path, ==, "/valid"); ++ ++ soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); ++ soup_server_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, "index", 5); ++} ++ ++#define CHUNKED_FORMAT_REQUEST "POST /valid HTTP/1.1\r\nHost: 127.0.0.1\r\n%sGET /invalid HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n" ++ ++static void ++do_chunked_test (ServerData *sd, gconstpointer test_data) ++{ ++ gint i; ++ struct { ++ const char *description; ++ const char *test; ++ } tests[] = { ++ { "Single LF", "Transfer-Encoding: chunked\r\n\r\n5;ext\n data\r\n0\r\n\r\n" }, ++ { "Content-Length and Transfer-Encoding", "Content-Length: 4\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n" }, ++ { "Content-Length and Transfer-Encoding with keep alive connection", "Content-Length: 4\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\n\r\n0\r\n\r\n" }, ++ }; ++ ++ 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_chunked_hundler, NULL, NULL); ++ ++ for (i = 0; i < G_N_ELEMENTS (tests); i++) { ++ GSocketClient *client; ++ GSocketConnection *conn; ++ GInputStream *input; ++ GOutputStream *output; ++ char *request; ++ char buffer[4096]; ++ gssize nread; ++ 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); ++ ++ request = g_strdup_printf (CHUNKED_FORMAT_REQUEST, tests[i].test); ++ ++ output = g_io_stream_get_output_stream (G_IO_STREAM (conn)); ++ g_output_stream_write_all (output, request, strlen (request), NULL, NULL, NULL); ++ g_output_stream_close (output, NULL, NULL); ++ g_socket_shutdown (g_socket_connection_get_socket (G_SOCKET_CONNECTION (conn)), FALSE, TRUE, &error); ++ ++ input = g_io_stream_get_input_stream (G_IO_STREAM (conn)); ++ do { ++ nread = g_input_stream_read (input, buffer, sizeof(buffer), NULL, NULL); ++ } while (nread > 0); ++ ++ g_free (request); ++ g_object_unref (conn); ++ g_object_unref (client); ++ } ++} ++ + + int + main (int argc, char **argv) +@@ -1330,6 +1397,9 @@ main (int argc, char **argv) + g_test_add ("/server/steal/CONNECT", ServerData, NULL, + server_setup, do_steal_connect_test, server_teardown); + ++ g_test_add ("/server/chunked", ServerData, NULL, ++ NULL, do_chunked_test, server_teardown); ++ + ret = g_test_run (); + + test_cleanup (); +-- +2.45.4 + diff --git a/SPECS/libsoup/libsoup.spec b/SPECS/libsoup/libsoup.spec index 7a4b61361fc..3d207710624 100644 --- a/SPECS/libsoup/libsoup.spec +++ b/SPECS/libsoup/libsoup.spec @@ -2,7 +2,7 @@ Summary: libsoup HTTP client/server library Name: libsoup Version: %{BaseVersion}.4 -Release: 10%{?dist} +Release: 11%{?dist} License: GPLv2 Vendor: Microsoft Corporation Distribution: Mariner @@ -36,6 +36,9 @@ Patch17: CVE-2025-4476.patch Patch18: CVE-2025-4948.patch Patch19: CVE-2025-4969.patch Patch20: CVE-2025-11021.patch +Patch21: CVE-2026-1467.patch +Patch22: CVE-2026-1761.patch +Patch23: CVE-2026-1801.patch BuildRequires: meson @@ -148,6 +151,9 @@ find %{buildroot} -type f -name "*.la" -delete -print %defattr(-,root,root) %changelog +* Mon Feb 09 2026 Azure Linux Security Servicing Account - 3.0.4-11 +- Patch for CVE-2026-1801, CVE-2026-1761, CVE-2026-1467 + * Wed Oct 29 2025 Azure Linux Security Servicing Account - 3.0.4-10 - Patch for CVE-2025-11021