diff --git a/1001-fix-CVE-2025-32906_1.patch b/1001-fix-CVE-2025-32906_1.patch new file mode 100644 index 0000000000000000000000000000000000000000..d990af24a6ae5f1f604337be59c8af9b1b33b973 --- /dev/null +++ b/1001-fix-CVE-2025-32906_1.patch @@ -0,0 +1,58 @@ +From 1f509f31b6f8420a3661c3f990424ab7b9164931 Mon Sep 17 00:00:00 2001 +From: Patrick Griffis +Date: Tue, 11 Feb 2025 14:36:26 -0600 +Subject: [PATCH] headers: Handle parsing edge case + +This version number is specifically crafted to pass sanity checks allowing it to go one byte out of bounds. +--- + libsoup/soup-headers.c | 2 +- + tests/header-parsing-test.c | 12 ++++++++++++ + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c +index 85385cea..9d6d00a3 100644 +--- a/libsoup/soup-headers.c ++++ b/libsoup/soup-headers.c +@@ -225,7 +225,7 @@ soup_headers_parse_request (const char *str, + !g_ascii_isdigit (version[5])) + return SOUP_STATUS_BAD_REQUEST; + major_version = strtoul (version + 5, &p, 10); +- if (*p != '.' || !g_ascii_isdigit (p[1])) ++ if (p + 1 >= str + len || *p != '.' || !g_ascii_isdigit (p[1])) + return SOUP_STATUS_BAD_REQUEST; + minor_version = strtoul (p + 1, &p, 10); + version_end = p; +diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c +index 07ea2866..10ddb684 100644 +--- a/tests/header-parsing-test.c ++++ b/tests/header-parsing-test.c +@@ -6,6 +6,10 @@ typedef struct { + const char *name, *value; + } Header; + ++static char unterminated_http_version[] = { ++ 'G','E','T',' ','/',' ','H','T','T','P','/','1', '0', '0', '.' ++}; ++ + static struct RequestTest { + const char *description; + const char *bugref; +@@ -383,6 +387,14 @@ static struct RequestTest { + { { NULL } } + }, + ++ /* This couldn't be a C string as going one byte over would have been safe. */ ++ { "Long HTTP version terminating at missing minor version", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/404", ++ unterminated_http_version, sizeof (unterminated_http_version), ++ SOUP_STATUS_BAD_REQUEST, ++ NULL, NULL, -1, ++ { { NULL } } ++ }, ++ + { "Non-HTTP request", NULL, + "GET / SOUP/1.1\r\nHost: example.com\r\n", -1, + SOUP_STATUS_BAD_REQUEST, +-- +GitLab + + diff --git a/1002-fix-CVE-2025-32906_2.patch b/1002-fix-CVE-2025-32906_2.patch new file mode 100644 index 0000000000000000000000000000000000000000..4e46c3b7216cddd3cef43c85cb9c89574f3344de --- /dev/null +++ b/1002-fix-CVE-2025-32906_2.patch @@ -0,0 +1,79 @@ +From 28341942302fe9d4f5629777150996ccc0289bd3 Mon Sep 17 00:00:00 2001 +From: mgb01105731 +Date: Thu, 15 May 2025 02:56:22 -0400 +Subject: [PATCH 1/1] fix cve 2 + +--- + libsoup/soup-headers.c | 4 ++-- + tests/header-parsing-test.c | 15 +++++++++++++-- + 2 files changed, 15 insertions(+), 4 deletions(-) + +diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c +index 3b77938..5e14f0e 100644 +--- a/libsoup/soup-headers.c ++++ b/libsoup/soup-headers.c +@@ -192,7 +192,7 @@ soup_headers_parse_request (const char *str, + /* RFC 2616 4.1 "servers SHOULD ignore any empty line(s) + * received where a Request-Line is expected." + */ +- while ((*str == '\r' || *str == '\n') && len > 0) { ++ while (len > 0 && (*str == '\r' || *str == '\n')) { + str++; + len--; + } +@@ -376,7 +376,7 @@ soup_headers_parse_response (const char *str, + * after a response, which we then see prepended to the next + * response on that connection. + */ +- while ((*str == '\r' || *str == '\n') && len > 0) { ++ while (len > 0 && (*str == '\r' || *str == '\n')) { + str++; + len--; + } +diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c +index 09a5cbf..4396dca 100644 +--- a/tests/header-parsing-test.c ++++ b/tests/header-parsing-test.c +@@ -6,10 +6,15 @@ typedef struct { + const char *name, *value; + } Header; + ++/* These are not C strings to ensure going one byte over is not safe. */ + static char unterminated_http_version[] = { + 'G','E','T',' ','/',' ','H','T','T','P','/','1', '0', '0', '.' + }; + ++static char only_newlines[] = { ++ '\n', '\n', '\n', '\n' ++}; ++ + static struct RequestTest { + const char *description; + const char *bugref; +@@ -405,7 +410,6 @@ static struct RequestTest { + { { NULL } } + }, + +- /* This couldn't be a C string as going one byte over would have been safe. */ + { "Long HTTP version terminating at missing minor version", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/404", + unterminated_http_version, sizeof (unterminated_http_version), + SOUP_STATUS_BAD_REQUEST, +@@ -460,7 +464,14 @@ static struct RequestTest { + SOUP_STATUS_EXPECTATION_FAILED, + NULL, NULL, -1, + { { NULL } } +- } ++ }, ++ ++ { "Only newlines", NULL, ++ only_newlines, sizeof (only_newlines), ++ SOUP_STATUS_BAD_REQUEST, ++ NULL, NULL, -1, ++ { { NULL } } ++ } + }; + static const int num_reqtests = G_N_ELEMENTS (reqtests); + +-- +2.41.0 + diff --git a/1003-fix-CVE-2025-32914.patch b/1003-fix-CVE-2025-32914.patch new file mode 100644 index 0000000000000000000000000000000000000000..57730820a768742d1329eff8314df9a449018f42 --- /dev/null +++ b/1003-fix-CVE-2025-32914.patch @@ -0,0 +1,102 @@ +From 5cec33ebb4e9a5e5436b08bb3dd849185f35a62f Mon Sep 17 00:00:00 2001 +From: mgb01105731 +Date: Thu, 15 May 2025 03:06:24 -0400 +Subject: [PATCH 1/1] fix cve 32914 + +--- + 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 a7e550f..dd93973 100644 +--- a/libsoup/soup-multipart.c ++++ b/libsoup/soup-multipart.c +@@ -181,7 +181,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); + soup_buffer_free (flattened); +diff --git a/tests/multipart-test.c b/tests/multipart-test.c +index 64a5ebf..c46b320 100644 +--- a/tests/multipart-test.c ++++ b/tests/multipart-test.c +@@ -479,6 +479,62 @@ test_multipart (gconstpointer data) + g_main_loop_unref (loop); + } + ++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_free (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_free (headers); ++ g_bytes_unref (bytes); ++} ++ + int + main (int argc, char **argv) + { +@@ -508,6 +564,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 (); + +-- +2.41.0 + diff --git a/1004-fix-CVE-2025-32913.patch b/1004-fix-CVE-2025-32913.patch new file mode 100644 index 0000000000000000000000000000000000000000..54c0e881dd12e2f0898a9bda6f466dbc1688795e --- /dev/null +++ b/1004-fix-CVE-2025-32913.patch @@ -0,0 +1,69 @@ +From 0011a20b3c0e466fd57d9c183c904d3e94b05ba3 Mon Sep 17 00:00:00 2001 +From: mgb01105731 +Date: Thu, 15 May 2025 03:17:42 -0400 +Subject: [PATCH 1/1] fix cve 32913 + +--- + libsoup/soup-message-headers.c | 11 ++++++++--- + tests/header-parsing-test.c | 15 +++++++++++++++ + 2 files changed, 23 insertions(+), 3 deletions(-) + +diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c +index 39ad14a..16795c0 100644 +--- a/libsoup/soup-message-headers.c ++++ b/libsoup/soup-message-headers.c +@@ -1454,10 +1454,15 @@ soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs, + */ + if (params && g_hash_table_lookup_extended (*params, "filename", + &orig_key, &orig_value)) { +- char *filename = strrchr (orig_value, '/'); ++ if (orig_value) { ++ char *filename = strrchr (orig_value, '/'); + +- if (filename) +- g_hash_table_insert (*params, g_strdup (orig_key), filename + 1); ++ if (filename) ++ g_hash_table_insert (*params, g_strdup (orig_key), g_strdup (filename + 1)); ++ } else { ++ /* filename with no value isn't valid. */ ++ g_hash_table_remove (*params, "filename"); ++ } + } + return TRUE; + } +diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c +index 4396dca..72dc013 100644 +--- a/tests/header-parsing-test.c ++++ b/tests/header-parsing-test.c +@@ -1057,6 +1057,7 @@ do_param_list_tests (void) + #define RFC5987_TEST_HEADER_FALLBACK "attachment; filename*=Unknown''t%FF%FF%FFst.txt; filename=\"test.txt\"" + #define RFC5987_TEST_HEADER_NO_TYPE "filename=\"test.txt\"" + #define RFC5987_TEST_HEADER_NO_TYPE_2 "filename=\"test.txt\"; foo=bar" ++#define RFC5987_TEST_HEADER_EMPTY_FILENAME ";filename" + + static void + do_content_disposition_tests (void) +@@ -1158,6 +1159,20 @@ do_content_disposition_tests (void) + g_assert_cmpstr (parameter2, ==, "bar"); + g_hash_table_destroy (params); + ++ /* Empty filename */ ++ soup_message_headers_clear (hdrs); ++ soup_message_headers_append (hdrs, "Content-Disposition", ++ RFC5987_TEST_HEADER_EMPTY_FILENAME); ++ if (!soup_message_headers_get_content_disposition (hdrs, ++ &disposition, ++ ¶ms)) { ++ soup_test_assert (FALSE, "empty filename decoding FAILED"); ++ return; ++ } ++ g_free (disposition); ++ g_assert_false (g_hash_table_contains (params, "filename")); ++ g_hash_table_destroy (params); ++ + soup_message_headers_free (hdrs); + + /* Ensure that soup-multipart always quotes filename */ +-- +2.41.0 + diff --git a/1005-fix-CVE-2025-2784.patch b/1005-fix-CVE-2025-2784.patch new file mode 100644 index 0000000000000000000000000000000000000000..cc25aa0cf463fda61fa3bc3ed73641ff72e0dc16 --- /dev/null +++ b/1005-fix-CVE-2025-2784.patch @@ -0,0 +1,115 @@ +From 0f8801781897f600c03297409249e6f58101dec6 Mon Sep 17 00:00:00 2001 +From: mgb01105731 +Date: Thu, 15 May 2025 03:53:16 -0400 +Subject: [PATCH 1/1] fix cve CVE-2025-2784 + +--- + libsoup/soup-content-sniffer.c | 10 +++---- + tests/sniffing-test.c | 48 ++++++++++++++++++++++++++++++++++ + 2 files changed, 53 insertions(+), 5 deletions(-) + +diff --git a/libsoup/soup-content-sniffer.c b/libsoup/soup-content-sniffer.c +index 967ec61..6ae2cc3 100644 +--- a/libsoup/soup-content-sniffer.c ++++ b/libsoup/soup-content-sniffer.c +@@ -612,8 +612,11 @@ sniff_text_or_binary (SoupContentSniffer *sniffer, SoupBuffer *buffer) + } + + static gboolean +-skip_insignificant_space (const char *resource, int *pos, int resource_length) ++skip_insignificant_space (const char *resource, gsize *pos, gsize resource_length) + { ++ if (*pos >= resource_length) ++ return TRUE; ++ + while ((resource[*pos] == '\x09') || + (resource[*pos] == '\x20') || + (resource[*pos] == '\x0A') || +@@ -632,7 +635,7 @@ sniff_feed_or_html (SoupContentSniffer *sniffer, SoupBuffer *buffer) + { + const char *resource = (const char *)buffer->data; + int resource_length = MIN (512, buffer->length); +- int pos = 0; ++ gsize pos = 0; + + if (resource_length < 3) + goto text_html; +@@ -642,9 +645,6 @@ sniff_feed_or_html (SoupContentSniffer *sniffer, SoupBuffer *buffer) + pos = 3; + + look_for_tag: +- if (pos > resource_length) +- goto text_html; +- + if (skip_insignificant_space (resource, &pos, resource_length)) + goto text_html; + +diff --git a/tests/sniffing-test.c b/tests/sniffing-test.c +index d2aa86b..d8e7ed9 100644 +--- a/tests/sniffing-test.c ++++ b/tests/sniffing-test.c +@@ -436,6 +436,52 @@ test_disabled (gconstpointer data) + soup_uri_free (uri); + } + ++static const gsize MARKUP_LENGTH = strlen (""); ++ ++static void ++do_skip_whitespace_test (void) ++{ ++ SoupContentSniffer *sniffer = soup_content_sniffer_new (); ++ SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "http://example.org"); ++ const char *test_cases[] = { ++ "", ++ "response_headers, "text/html", NULL); ++ ++ for (guint i = 0; i < G_N_ELEMENTS (test_cases); i++) { ++ const char *trailing_data = test_cases[i]; ++ gsize leading_zeros = 512 - MARKUP_LENGTH - strlen (trailing_data); ++ gsize testsize = MARKUP_LENGTH + leading_zeros + strlen (trailing_data); ++ guint8 *data = g_malloc0 (testsize); ++ guint8 *p = data; ++ char *content_type; ++ GBytes *buffer; ++ ++ // Format of $trailing_data ++ memcpy (p, "", strlen ("-->")); ++ p += strlen ("-->"); ++ if (strlen (trailing_data)) ++ memcpy (p, trailing_data, strlen (trailing_data)); ++ // Purposefully not NUL terminated. ++ ++ buffer = g_bytes_new_take (g_steal_pointer (&data), testsize); ++ content_type = soup_content_sniffer_sniff (sniffer, msg, buffer, NULL); ++ ++ g_free (content_type); ++ g_bytes_unref (buffer); ++ } ++ ++ g_object_unref (msg); ++ g_object_unref (sniffer); ++} ++ + int + main (int argc, char **argv) + { +@@ -610,6 +656,8 @@ main (int argc, char **argv) + "/text_or_binary/home.gif", + test_disabled); + ++ g_test_add_func ("/sniffing/whitespace", do_skip_whitespace_test); ++ + ret = g_test_run (); + + soup_uri_free (base_uri); +-- +2.41.0 + diff --git a/libsoup.spec b/libsoup.spec index 8fae45882211b46656cd6ee7c4f7c3e00e5b8ca7..d94f20b761a162b24d090d5fa31f4d5eb66bfc0b 100644 --- a/libsoup.spec +++ b/libsoup.spec @@ -1,4 +1,4 @@ -%define anolis_release 6 +%define anolis_release 7 %define glib2_version 2.58 %{!?with_docs: %global with_docs 1} @@ -16,6 +16,11 @@ Patch1: 0001-bugfix-for-CVE-2025-32907.patch Patch2: 0002-bugfix-for-CVE-2025-32909.patch Patch3: 0003-bugfix-for-CVE-2025-32910.patch Patch4: 0004-bugfix-for-CVE-2025-32910.patch +Patch5: 1001-fix-CVE-2025-32906_1.patch +Patch6: 1002-fix-CVE-2025-32906_2.patch +Patch7: 1003-fix-CVE-2025-32914.patch +Patch8: 1004-fix-CVE-2025-32913.patch +Patch9: 1005-fix-CVE-2025-2784.patch BuildRequires: gettext BuildRequires: glib-networking @@ -125,6 +130,9 @@ This package contains developer documentation for %{name}. %endif %changelog +* Mon Jun 09 2025 mgb01105731 - 2.74.3-7 +- Add patch to fix CVE-2025-32906 CVE-2025-32914 CVE-2025-32913 CVE-2025-2784 + * Fri Jun 06 2025 tomcruiseqi <10762123+tomcruiseqi@user.noreply.gitee.com> - 2.74.3-6 - fix CVE-2025-32910