diff --git a/CVE-2025-32049.patch b/CVE-2025-32049.patch new file mode 100644 index 0000000000000000000000000000000000000000..7644066b4768bf93a1bca1125c4292b09058de2d --- /dev/null +++ b/CVE-2025-32049.patch @@ -0,0 +1,16 @@ +diff --git a/libsoup/websocket/soup-websocket-connection.c b/libsoup/websocket/soup-websocket-connection.c +index a1448134..48b08b60 100644 +--- a/libsoup/websocket/soup-websocket-connection.c ++++ b/libsoup/websocket/soup-websocket-connection.c +@@ -971,6 +971,11 @@ process_contents (SoupWebsocketConnection *self, + switch (priv->message_opcode) { + case 0x01: + case 0x02: ++ /* Safety valve */ ++ if (priv->message_data->len + payload_len > priv->max_incoming_payload_size) { ++ too_big_error_and_close (self, (priv->message_data->len + payload_len)); ++ return; ++ } + g_byte_array_append (priv->message_data, payload, payload_len); + break; + default: diff --git a/CVE-2025-32908.patch b/CVE-2025-32908.patch new file mode 100644 index 0000000000000000000000000000000000000000..4b6bcb639cb61667557688016f046857c778f8ba --- /dev/null +++ b/CVE-2025-32908.patch @@ -0,0 +1,130 @@ +From a792b23ab87cacbf4dd9462bf7b675fa678efbae Mon Sep 17 00:00:00 2001 +From: Milan Crha +Date: Tue, 15 Apr 2025 09:59:05 +0200 +Subject: [PATCH] soup-server-http2: Check validity of the constructed + connection URI + +The HTTP/2 pseudo-headers can contain invalid values, which the GUri rejects +and returns NULL, but the soup-server did not check the validity and could +abort the server itself later in the code. + +Closes #429 +--- + .../http2/soup-server-message-io-http2.c | 4 +++ + tests/http2-test.c | 28 +++++++++++++++++++ + 2 files changed, 32 insertions(+) + +diff --git a/libsoup/server/http2/soup-server-message-io-http2.c b/libsoup/server/http2/soup-server-message-io-http2.c +index 943ecfd3..f1fe2d5c 100644 +--- a/libsoup/server/http2/soup-server-message-io-http2.c ++++ b/libsoup/server/http2/soup-server-message-io-http2.c +@@ -771,9 +771,13 @@ on_frame_recv_callback (nghttp2_session *session, + char *uri_string; + GUri *uri; + ++ if (msg_io->scheme == NULL || msg_io->authority == NULL || msg_io->path == NULL) ++ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + uri_string = g_strdup_printf ("%s://%s%s", msg_io->scheme, msg_io->authority, msg_io->path); + uri = g_uri_parse (uri_string, SOUP_HTTP_URI_FLAGS, NULL); + g_free (uri_string); ++ if (uri == NULL) ++ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + soup_server_message_set_uri (msg_io->msg, uri); + g_uri_unref (uri); + +diff --git a/tests/http2-test.c b/tests/http2-test.c +index 5b6da5e4..ec7972fe 100644 +--- a/tests/http2-test.c ++++ b/tests/http2-test.c +@@ -1341,6 +1341,30 @@ do_connection_closed_test (Test *test, gconstpointer data) + g_uri_unref (uri); + } + ++static void ++do_broken_pseudo_header_test (Test *test, gconstpointer data) ++{ ++ char *path; ++ SoupMessage *msg; ++ GUri *uri; ++ GBytes *body = NULL; ++ GError *error = NULL; ++ ++ uri = g_uri_parse_relative (base_uri, "/ag", SOUP_HTTP_URI_FLAGS, NULL); ++ ++ /* an ugly cheat to construct a broken URI, which can be sent from other libs */ ++ path = (char *) g_uri_get_path (uri); ++ path[1] = '%'; ++ ++ msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); ++ body = soup_test_session_async_send (test->session, msg, NULL, &error); ++ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT); ++ g_assert_null (body); ++ g_clear_error (&error); ++ g_object_unref (msg); ++ g_uri_unref (uri); ++} ++ + static gboolean + unpause_message (SoupServerMessage *msg) + { +@@ -1662,6 +1686,10 @@ main (int argc, char **argv) + setup_session, + do_connection_closed_test, + teardown_session); ++ g_test_add ("/http2/broken-pseudo-header", Test, NULL, ++ setup_session, ++ do_broken_pseudo_header_test, ++ teardown_session); + + ret = g_test_run (); + +-- +GitLab + +From 527428a033df573ef4558ce1106e080fd9ec5c71 Mon Sep 17 00:00:00 2001 +From: Milan Crha +Date: Mon, 28 Apr 2025 10:55:42 +0200 +Subject: [PATCH] soup-server-http2: Correct check of the validity of the + constructed connection URI + +RFC 5740: the CONNECT has unset the "scheme" and "path", thus allow them unset. + +The commit a792b23ab87cacbf4dd9462bf7b675fa678efbae also missed to decrement +the `io->in_callback` in the early returns. + +Related to #429 +--- + .../server/http2/soup-server-message-io-http2.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/libsoup/server/http2/soup-server-message-io-http2.c b/libsoup/server/http2/soup-server-message-io-http2.c +index f1fe2d5c..913afb46 100644 +--- a/libsoup/server/http2/soup-server-message-io-http2.c ++++ b/libsoup/server/http2/soup-server-message-io-http2.c +@@ -771,13 +771,18 @@ on_frame_recv_callback (nghttp2_session *session, + char *uri_string; + GUri *uri; + +- if (msg_io->scheme == NULL || msg_io->authority == NULL || msg_io->path == NULL) +- return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +- uri_string = g_strdup_printf ("%s://%s%s", msg_io->scheme, msg_io->authority, msg_io->path); ++ if (msg_io->authority == NULL) { ++ io->in_callback--; ++ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; ++ } ++ /* RFC 5740: the CONNECT has unset the "scheme" and "path", but the GUri requires the scheme, thus let it be "(null)" */ ++ uri_string = g_strdup_printf ("%s://%s%s", msg_io->scheme, msg_io->authority, msg_io->path == NULL ? "" : msg_io->path); + uri = g_uri_parse (uri_string, SOUP_HTTP_URI_FLAGS, NULL); + g_free (uri_string); +- if (uri == NULL) +- return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; ++ if (uri == NULL) { ++ io->in_callback--; ++ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; ++ } + soup_server_message_set_uri (msg_io->msg, uri); + g_uri_unref (uri); + +-- +GitLab + diff --git a/CVE-2025-32914.patch b/CVE-2025-32914.patch new file mode 100644 index 0000000000000000000000000000000000000000..5ec88a9fb0c296a3c6dccdffcc08296dd41bc4d6 --- /dev/null +++ b/CVE-2025-32914.patch @@ -0,0 +1,107 @@ +From 5bfcf8157597f2d327050114fb37ff600004dbcf Mon Sep 17 00:00:00 2001 +From: Milan Crha +Date: Tue, 15 Apr 2025 09:03:00 +0200 +Subject: [PATCH] multipart: Fix read out of buffer bounds under + soup_multipart_new_from_message() + +This is CVE-2025-32914, special crafted input can cause read out of buffer bounds +of the body argument. + +Closes #436 +--- + libsoup/soup-multipart.c | 2 +- + tests/multipart-test.c | 58 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 59 insertions(+), 1 deletion(-) + +diff --git a/libsoup/soup-multipart.c b/libsoup/soup-multipart.c +index 2421c91f8..102ce3722 100644 +--- a/libsoup/soup-multipart.c ++++ b/libsoup/soup-multipart.c +@@ -173,7 +173,7 @@ soup_multipart_new_from_message (SoupMessageHeaders *headers, + return NULL; + } + +- split = strstr (start, "\r\n\r\n"); ++ split = g_strstr_len (start, body_end - start, "\r\n\r\n"); + if (!split || split > end) { + soup_multipart_free (multipart); + return NULL; +diff --git a/tests/multipart-test.c b/tests/multipart-test.c +index 2c0e7e969..f5b986889 100644 +--- a/tests/multipart-test.c ++++ b/tests/multipart-test.c +@@ -471,6 +471,62 @@ test_multipart (gconstpointer data) + loop = NULL; + } + ++static void ++test_multipart_bounds_good (void) ++{ ++ #define TEXT "line1\r\nline2" ++ SoupMultipart *multipart; ++ SoupMessageHeaders *headers, *set_headers = NULL; ++ GBytes *bytes, *set_bytes = NULL; ++ const char *raw_data = "--123\r\nContent-Type: text/plain;\r\n\r\n" TEXT "\r\n--123--\r\n"; ++ gboolean success; ++ ++ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); ++ soup_message_headers_append (headers, "Content-Type", "multipart/mixed; boundary=\"123\""); ++ ++ bytes = g_bytes_new (raw_data, strlen (raw_data)); ++ ++ multipart = soup_multipart_new_from_message (headers, bytes); ++ ++ g_assert_nonnull (multipart); ++ g_assert_cmpint (soup_multipart_get_length (multipart), ==, 1); ++ success = soup_multipart_get_part (multipart, 0, &set_headers, &set_bytes); ++ g_assert_true (success); ++ g_assert_nonnull (set_headers); ++ g_assert_nonnull (set_bytes); ++ g_assert_cmpint (strlen (TEXT), ==, g_bytes_get_size (set_bytes)); ++ g_assert_cmpstr ("text/plain", ==, soup_message_headers_get_content_type (set_headers, NULL)); ++ g_assert_cmpmem (TEXT, strlen (TEXT), g_bytes_get_data (set_bytes, NULL), g_bytes_get_size (set_bytes)); ++ ++ soup_message_headers_unref (headers); ++ g_bytes_unref (bytes); ++ ++ soup_multipart_free (multipart); ++ ++ #undef TEXT ++} ++ ++static void ++test_multipart_bounds_bad (void) ++{ ++ SoupMultipart *multipart; ++ SoupMessageHeaders *headers; ++ GBytes *bytes; ++ const char *raw_data = "--123\r\nContent-Type: text/plain;\r\nline1\r\nline2\r\n--123--\r\n"; ++ ++ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); ++ soup_message_headers_append (headers, "Content-Type", "multipart/mixed; boundary=\"123\""); ++ ++ bytes = g_bytes_new (raw_data, strlen (raw_data)); ++ ++ /* it did read out of raw_data/bytes bounds */ ++ multipart = soup_multipart_new_from_message (headers, bytes); ++ g_assert_null (multipart); ++ ++ soup_message_headers_unref (headers); ++ g_bytes_unref (bytes); ++} ++ + int + main (int argc, char **argv) + { +@@ -498,6 +554,8 @@ main (int argc, char **argv) + g_test_add_data_func ("/multipart/sync", GINT_TO_POINTER (SYNC_MULTIPART), test_multipart); + g_test_add_data_func ("/multipart/async", GINT_TO_POINTER (ASYNC_MULTIPART), test_multipart); + g_test_add_data_func ("/multipart/async-small-reads", GINT_TO_POINTER (ASYNC_MULTIPART_SMALL_READS), test_multipart); ++ g_test_add_func ("/multipart/bounds-good", test_multipart_bounds_good); ++ g_test_add_func ("/multipart/bounds-bad", test_multipart_bounds_bad); + + ret = g_test_run (); + +-- +GitLab + diff --git a/CVE-2025-4035.patch b/CVE-2025-4035.patch new file mode 100644 index 0000000000000000000000000000000000000000..06e0291c637d8fb49973f41ec40b6115b66085a4 --- /dev/null +++ b/CVE-2025-4035.patch @@ -0,0 +1,231 @@ +From 845e198402377f8644e9ae5200f1715df1ddfc08 Mon Sep 17 00:00:00 2001 +From: Patrick Griffis +Date: Thu, 27 Mar 2025 14:43:26 -0500 +Subject: [PATCH 1/3] cookie: Always normalize domain value + +In order for libpsl to give accurate results the domain must be lowercased. + +To make it easiest we normalize it at construction time of the cookie. +--- + libsoup/cookies/soup-cookie-jar.c | 11 ++++++++--- + libsoup/cookies/soup-cookie.c | 22 +++++++++++++++++++--- + libsoup/soup-tld.c | 11 ++++++++++- + libsoup/soup-uri-utils-private.h | 2 ++ + libsoup/soup-uri-utils.c | 18 ++++++++++++++++++ + tests/cookies-test.c | 19 +++++++++++++++++++ + 6 files changed, 76 insertions(+), 7 deletions(-) + +diff --git a/libsoup/cookies/soup-cookie-jar.c b/libsoup/cookies/soup-cookie-jar.c +index fac53a5f9..7f92ace1f 100644 +--- a/libsoup/cookies/soup-cookie-jar.c ++++ b/libsoup/cookies/soup-cookie-jar.c +@@ -519,6 +519,7 @@ incoming_cookie_is_third_party (SoupCookieJar *jar, + { + SoupCookieJarPrivate *priv; + const char *normalized_cookie_domain; ++ char *normalized_first_party_host; + const char *cookie_base_domain; + const char *first_party_base_domain; + const char *first_party_host; +@@ -540,12 +541,16 @@ incoming_cookie_is_third_party (SoupCookieJar *jar, + if (cookie_base_domain == NULL) + cookie_base_domain = soup_cookie_get_domain (cookie); + +- first_party_base_domain = soup_tld_get_base_domain (first_party_host, NULL); ++ normalized_first_party_host = soup_uri_normalize_domain (first_party_host); ++ first_party_base_domain = soup_tld_get_base_domain (normalized_first_party_host, NULL); + if (first_party_base_domain == NULL) +- first_party_base_domain = first_party_host; ++ first_party_base_domain = normalized_first_party_host; + +- if (soup_host_matches_host (cookie_base_domain, first_party_base_domain)) ++ if (soup_host_matches_host (cookie_base_domain, first_party_base_domain)) { ++ g_free (normalized_first_party_host); + return FALSE; ++ } ++ g_free (normalized_first_party_host); + + if (policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY) + return TRUE; +diff --git a/libsoup/cookies/soup-cookie.c b/libsoup/cookies/soup-cookie.c +index cc80d001f..b6d771646 100644 +--- a/libsoup/cookies/soup-cookie.c ++++ b/libsoup/cookies/soup-cookie.c +@@ -168,6 +168,16 @@ parse_date (const char **val_p) + return date; + } + ++static gboolean ++is_lowercase_ascii_string (const char *str) ++{ ++ for (; *str; str++) { ++ if (!g_ascii_islower (*str)) ++ return FALSE; ++ } ++ return TRUE; ++} ++ + #define MAX_AGE_CAP_IN_SECONDS 31536000 // 1 year + #define MAX_ATTRIBUTE_SIZE 1024 + +@@ -311,6 +321,12 @@ parse_one_cookie (const char *header, GUri *origin) + g_free (cookie->domain); + cookie->domain = tmp; + } ++ ++ if (!is_lowercase_ascii_string (cookie->domain)) { ++ char *tmp = soup_uri_normalize_domain (cookie->domain); ++ g_free (cookie->domain); ++ cookie->domain = tmp; ++ } + } + + if (origin) { +@@ -321,7 +337,7 @@ parse_one_cookie (const char *header, GUri *origin) + return NULL; + } + } else +- cookie->domain = g_strdup (g_uri_get_host (origin)); ++ cookie->domain = soup_uri_normalize_domain (g_uri_get_host (origin)); + + /* The original cookie spec didn't say that pages + * could only set cookies for paths they were under. +@@ -364,7 +380,7 @@ cookie_new_internal (const char *name, const char *value, + cookie = g_slice_new0 (SoupCookie); + cookie->name = g_strdup (name); + cookie->value = g_strdup (value); +- cookie->domain = g_strdup (domain); ++ cookie->domain = soup_uri_normalize_domain (domain); + cookie->path = g_strdup (path); + soup_cookie_set_max_age (cookie, max_age); + cookie->same_site_policy = SOUP_SAME_SITE_POLICY_LAX; +@@ -537,7 +553,7 @@ void + soup_cookie_set_domain (SoupCookie *cookie, const char *domain) + { + g_free (cookie->domain); +- cookie->domain = g_strdup (domain); ++ cookie->domain = soup_uri_normalize_domain (domain); + } + + /** +diff --git a/libsoup/soup-tld.c b/libsoup/soup-tld.c +index 2d8151662..71fac5749 100644 +--- a/libsoup/soup-tld.c ++++ b/libsoup/soup-tld.c +@@ -15,6 +15,7 @@ + #include + + #include "soup-tld.h" ++#include "soup-uri-utils-private.h" + #include "soup.h" + + static const char *soup_tld_get_base_domain_internal (const char *hostname, +@@ -41,6 +42,8 @@ static const char *soup_tld_get_base_domain_internal (const char *hostname, + * UTF-8 or ASCII format (and the return value will be in the same + * format). + * ++ * For accurate results @hostname must be lowercase. ++ * + * Returns: a pointer to the start of the base domain in @hostname. If + * an error occurs, %NULL will be returned and @error set. + **/ +@@ -80,6 +83,8 @@ gboolean + soup_tld_domain_is_public_suffix (const char *domain) + { + const psl_ctx_t* psl = soup_psl_context (); ++ char *normalized; ++ gboolean is_public_suffix; + + g_return_val_if_fail (domain, FALSE); + +@@ -88,7 +93,11 @@ soup_tld_domain_is_public_suffix (const char *domain) + return FALSE; + } + +- return psl_is_public_suffix2 (psl, domain, PSL_TYPE_ANY | PSL_TYPE_NO_STAR_RULE); ++ normalized = soup_uri_normalize_domain (domain); ++ is_public_suffix = psl_is_public_suffix2 (psl, normalized, PSL_TYPE_ANY | PSL_TYPE_NO_STAR_RULE); ++ g_free (normalized); ++ ++ return is_public_suffix; + } + + /** +diff --git a/libsoup/soup-uri-utils-private.h b/libsoup/soup-uri-utils-private.h +index 0119f0814..c2f984f86 100644 +--- a/libsoup/soup-uri-utils-private.h ++++ b/libsoup/soup-uri-utils-private.h +@@ -28,6 +28,8 @@ GUri *soup_uri_copy_with_normalized_flags (GUri *uri); + + char *soup_uri_get_host_for_headers (GUri *uri); + ++char *soup_uri_normalize_domain (const char *domain); ++ + #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 0963a1143..1f65faede 100644 +--- a/libsoup/soup-uri-utils.c ++++ b/libsoup/soup-uri-utils.c +@@ -506,3 +506,21 @@ soup_uri_get_host_for_headers (GUri *uri) + + return g_strdup (host); + } ++ ++char * ++soup_uri_normalize_domain (const char *domain) ++{ ++ char *lower; ++ char *normalized; ++ ++ g_assert (domain); ++ ++ if (g_str_is_ascii (domain)) ++ return g_ascii_strdown (domain, -1); ++ ++ lower = g_utf8_casefold (domain, -1); ++ normalized = g_utf8_normalize (lower, -1, G_NORMALIZE_NFKC); ++ g_free (lower); ++ ++ return normalized; ++} +diff --git a/tests/cookies-test.c b/tests/cookies-test.c +index 1d2d45630..7007aaf59 100644 +--- a/tests/cookies-test.c ++++ b/tests/cookies-test.c +@@ -695,6 +695,24 @@ do_cookies_threads_test (void) + soup_test_session_abort_unref (session); + } + ++static void ++do_cookies_public_suffix_test (void) ++{ ++ SoupCookieJar *jar = soup_cookie_jar_new (); ++ GUri *uri = g_uri_parse ("http://example.CO.uk", SOUP_HTTP_URI_FLAGS, NULL); ++ GSList *cookies; ++ ++ soup_cookie_jar_set_cookie (jar, uri, "value=1; domain=.co.uk"); ++ soup_cookie_jar_set_cookie (jar, uri, "value=1; domain=.CO.uk"); ++ soup_cookie_jar_set_cookie (jar, uri, "value=1; domain=.CO.UK"); ++ ++ cookies = soup_cookie_jar_all_cookies (jar); ++ g_assert_cmpint (g_slist_length (cookies), ==, 0); ++ ++ g_uri_unref (uri); ++ g_object_unref (jar); ++} ++ + int + main (int argc, char **argv) + { +@@ -726,6 +744,7 @@ main (int argc, char **argv) + g_test_add_func ("/cookies/secure-cookies", do_cookies_strict_secure_test); + g_test_add_func ("/cookies/prefix", do_cookies_prefix_test); + g_test_add_func ("/cookies/threads", do_cookies_threads_test); ++ g_test_add_func ("/cookies/public-suffix", do_cookies_public_suffix_test); + + ret = g_test_run (); + +-- +GitLab diff --git a/CVE-2025-4948.patch b/CVE-2025-4948.patch new file mode 100644 index 0000000000000000000000000000000000000000..cb71c6aff1ff8c060ecc9a30afef6db0b0194353 --- /dev/null +++ b/CVE-2025-4948.patch @@ -0,0 +1,89 @@ +From 66521f00e9f87f709d8ad9138f19052db933cf06 Mon Sep 17 00:00:00 2001 +From: Milan Crha +Date: Thu, 15 May 2025 17:49:11 +0200 +Subject: [PATCH] soup-multipart: Verify boundary limits for multipart body + +It could happen that the boundary started at a place which resulted into +a negative number, which in an unsigned integer is a very large value. +Check the body size is not a negative value before setting it. + +Closes https://gitlab.gnome.org/GNOME/libsoup/-/issues/449 +--- + libsoup/soup-multipart.c | 2 +- + tests/multipart-test.c | 40 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 41 insertions(+), 1 deletion(-) + +diff --git a/libsoup/soup-multipart.c b/libsoup/soup-multipart.c +index 102ce372..a587fe7c 100644 +--- a/libsoup/soup-multipart.c ++++ b/libsoup/soup-multipart.c +@@ -204,7 +204,7 @@ soup_multipart_new_from_message (SoupMessageHeaders *headers, + */ + part_body = g_bytes_new_from_bytes (body, // FIXME + split - body_data, +- end - 2 - split); ++ end - 2 >= split ? end - 2 - split : 0); + g_ptr_array_add (multipart->bodies, part_body); + + start = end; +diff --git a/tests/multipart-test.c b/tests/multipart-test.c +index f5b98688..92b673eb 100644 +--- a/tests/multipart-test.c ++++ b/tests/multipart-test.c +@@ -527,6 +527,45 @@ test_multipart_bounds_bad (void) + g_bytes_unref (bytes); + } + ++static void ++test_multipart_too_large (void) ++{ ++ const char *raw_body = ++ "-------------------\r\n" ++ "-\n" ++ "Cont\"\r\n" ++ "Content-Tynt----e:n\x8erQK\r\n" ++ "Content-Disposition: name= form-; name=\"file\"; filename=\"ype:i/ -d; ----\xae\r\n" ++ "Content-Typimag\x01/png--\\\n" ++ "\r\n" ++ "---:\n\r\n" ++ "\r\n" ++ "-------------------------------------\r\n" ++ "---------\r\n" ++ "----------------------"; ++ GBytes *body; ++ GHashTable *params; ++ SoupMessageHeaders *headers; ++ SoupMultipart *multipart; ++ ++ params = g_hash_table_new (g_str_hash, g_str_equal); ++ g_hash_table_insert (params, (gpointer) "boundary", (gpointer) "-----------------"); ++ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); ++ soup_message_headers_set_content_type (headers, "multipart/form-data", params); ++ g_hash_table_unref (params); ++ ++ body = g_bytes_new_static (raw_body, strlen (raw_body)); ++ multipart = soup_multipart_new_from_message (headers, body); ++ soup_message_headers_unref (headers); ++ g_bytes_unref (body); ++ ++ g_assert_nonnull (multipart); ++ g_assert_cmpint (soup_multipart_get_length (multipart), ==, 1); ++ g_assert_true (soup_multipart_get_part (multipart, 0, &headers, &body)); ++ g_assert_cmpint (g_bytes_get_size (body), ==, 0); ++ soup_multipart_free (multipart); ++} ++ + int + main (int argc, char **argv) + { +@@ -556,6 +595,7 @@ main (int argc, char **argv) + g_test_add_data_func ("/multipart/async-small-reads", GINT_TO_POINTER (ASYNC_MULTIPART_SMALL_READS), test_multipart); + 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/too-large", test_multipart_too_large); + + ret = g_test_run (); + +-- +GitLab + diff --git a/http2-body-size-test-timeout.patch b/http2-body-size-test-timeout.patch new file mode 100644 index 0000000000000000000000000000000000000000..b031b18e23cdf037045bf0e8786b08f9b13eaa93 --- /dev/null +++ b/http2-body-size-test-timeout.patch @@ -0,0 +1,30 @@ +From a2eaa4aab2b62976118a4a62f5041eead5c90a02 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 1 May 2025 08:57:46 -0500 +Subject: [PATCH] Reduce runtime of http2-body-size test + +This test is *really* slow and I think it would be excessive to increase +the test timeout any further, so let's test less data. + +Fixes #444 +Obsoletes: !309 +--- + tests/http2-body-stream-test.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/http2-body-stream-test.c b/tests/http2-body-stream-test.c +index 540beb3b..c53f22bc 100644 +--- a/tests/http2-body-stream-test.c ++++ b/tests/http2-body-stream-test.c +@@ -24,7 +24,7 @@ static void + do_large_data_test (void) + { + #define CHUNK_SIZE ((gsize)1024 * 1024 * 512) // 512 MiB +-#define TEST_SIZE (CHUNK_SIZE * 20) // 10 GiB ++#define TEST_SIZE (CHUNK_SIZE * 4) // 2 GiB + + GInputStream *stream = soup_body_input_stream_http2_new (); + SoupBodyInputStreamHttp2 *mem_stream = SOUP_BODY_INPUT_STREAM_HTTP2 (stream); +-- +GitLab + diff --git a/libsoup3.spec b/libsoup3.spec index cda50cd57a5a652a08a2158dbade4e40835d399e..c63a3be850ba81b341a1255608d69603bd563502 100644 --- a/libsoup3.spec +++ b/libsoup3.spec @@ -1,22 +1,29 @@ -%define anolis_release 3 +%define anolis_release 4 %global glib2_version 2.69.1 %{!?with_docs: %global with_docs 1} Name: libsoup3 -Version: 3.6.5 +Version: 3.6.5 Release: %{anolis_release}%{dist} Summary: Soup, an HTTP library implementation License: LGPL-2.0-or-later URL: https://wiki.gnome.org/Projects/libsoup -Source0: https://download.gnome.org/sources/libsoup/3.6/libsoup-%{version}.tar.xz +Source0: https://download.gnome.org/sources/libsoup/3.6/libsoup-3.6.5.tar.xz # From https://gitlab.com/redhat/centos-stream/rpms/libsoup3/-/tree/c10s?ref_type=heads Patch0: 1000-CVE-2025-4948.patch # https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/452/diffs?commit_id=9bb92f7a685e31e10e9e8221d0342280432ce836 Patch1: CVE-2025-32907.patch +Patch2: CVE-2025-4948.patch +Patch3: CVE-2025-4035.patch +Patch4: http2-body-size-test-timeout.patch +Patch5: CVE-2025-32908.patch +Patch6: CVE-2025-32914.patch +Patch7: server-test-timeout.patch +Patch8: CVE-2025-32049.patch BuildRequires: gcc BuildRequires: gettext @@ -34,6 +41,8 @@ BuildRequires: pkgconfig(libpsl) BuildRequires: pkgconfig(sqlite3) BuildRequires: pkgconfig(sysprof-capture-4) BuildRequires: /usr/bin/ntlm_auth +BuildRequires: ca-certificates +BuildRequires: pkgconfig(gnutls) Recommends: glib-networking >= %{glib2_version} @@ -114,6 +123,14 @@ install -m 644 -D tests/libsoup.supp %{buildroot}%{_datadir}/libsoup-3.0/libsoup %doc README NEWS AUTHORS %changelog +* Thu Sep 18 2025 wenyuzifang - 3.6.5-4 +- Fix integer underflow in multipart parsing to prevent crashes and security vulnerabilities from malformed input. +- Ensure cookie domains are normalized to prevent incorrect PSL lookups and enhance security. +- Reduce test runtime to prevent timeouts and improve efficiency without losing meaningful coverage. +- Prevent crashes on invalid HTTP/2 requests and ensure compliance with RFC 7540 by safely handling pseudo-headers. +- Apply patch to prevent out-of-bounds reads and enhance security against malicious multipart inputs. +- Fix deadlock by ensuring proper mutex locking before modifying shared data and signaling threads. +- Prevent memory exhaustion by enforcing message size limits during WebSocket fragment reassembly. * Thu Jul 31 2025 wenxin - 3.6.5-3 - add patch to fix CVE-2025-32907 diff --git a/server-test-timeout.patch b/server-test-timeout.patch new file mode 100644 index 0000000000000000000000000000000000000000..90795238c991293c57c61e8f317f45b6c7d1c78c --- /dev/null +++ b/server-test-timeout.patch @@ -0,0 +1,43 @@ +From 9ff306aa714efd06ceeafacee03298a3665055b1 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 30 Apr 2025 14:13:41 -0500 +Subject: [PATCH] test-utils: fix deadlock in add_listener_in_thread() + +The mutex is locked in the wrong place here. + +Hopefully fixes #379 +--- + tests/test-utils.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/tests/test-utils.c b/tests/test-utils.c +index df4cee44..5c1e316c 100644 +--- a/tests/test-utils.c ++++ b/tests/test-utils.c +@@ -607,9 +607,11 @@ static gboolean + add_listener_in_thread (gpointer user_data) + { + AddListenerData *data = user_data; ++ GUri *uri; + +- data->uri = add_listener (data->server, data->scheme, data->host); ++ uri = add_listener (data->server, data->scheme, data->host); + g_mutex_lock (&data->mutex); ++ data->uri = uri; + g_cond_signal (&data->cond); + g_mutex_unlock (&data->mutex); + +@@ -641,9 +643,9 @@ soup_test_server_get_uri (SoupServer *server, + data.host = host; + data.uri = NULL; + +- g_mutex_lock (&data.mutex); + soup_add_completion (context, add_listener_in_thread, &data); + ++ g_mutex_lock (&data.mutex); + while (!data.uri) + g_cond_wait (&data.cond, &data.mutex); + +-- +GitLab +