diff options
author | Maxim Cournoyer <maxim@guixotic.coop> | 2025-07-31 08:02:12 +0900 |
---|---|---|
committer | Maxim Cournoyer <maxim@guixotic.coop> | 2025-07-31 09:10:38 +0900 |
commit | 55cd48b0beed28462c303f258d8b85e1d866218f (patch) | |
tree | b1ef9e48c25c92547e0d4b2fa464bb07c47ed24e | |
parent | 7e535a9194cd7c6a4c1494431e4dca03423fae40 (diff) |
gnu: libsoup: Apply patches fixing deadlocks and CVE-2025-4476.
The patches are a subset taken from Debian (see:
<https://sources.debian.org/patches/libsoup3/3.6.5-3/>).
* gnu/packages/patches/libsoup-auth-digest-fix-crash.patch
* gnu/packages/patches/libsoup-deadlock-in-add_listener_in_thread.patch
* gnu/packages/patches/libsoup-fix-merge-of-ranges.patch
* gnu/packages/patches/libsoup-memory-leak-in-soup_form_decode.patch
* gnu/packages/patches/libsoup-multipart-bounds-check.patch
* gnu/packages/patches/libsoup-use-libdl-instead-of-gmodule.patch: New files.
* gnu/local.mk (dist_patch_DATA): Register them.
* gnu/packages/gnome.scm (libsoup-minimal): Apply them.
Change-Id: I7e4968c1d87e28860fc68616f6107d018e0d93dd
-rw-r--r-- | gnu/local.mk | 8 | ||||
-rw-r--r-- | gnu/packages/gnome.scm | 18 | ||||
-rw-r--r-- | gnu/packages/patches/libsoup-auth-digest-fix-crash.patch | 31 | ||||
-rw-r--r-- | gnu/packages/patches/libsoup-deadlock-in-add_listener_in_thread.patch | 42 | ||||
-rw-r--r-- | gnu/packages/patches/libsoup-fix-merge-of-ranges.patch | 192 | ||||
-rw-r--r-- | gnu/packages/patches/libsoup-memory-leak-in-soup_form_decode.patch | 101 | ||||
-rw-r--r-- | gnu/packages/patches/libsoup-multipart-bounds-check.patch | 106 | ||||
-rw-r--r-- | gnu/packages/patches/libsoup-use-libdl-instead-of-gmodule.patch | 70 |
8 files changed, 562 insertions, 6 deletions
diff --git a/gnu/local.mk b/gnu/local.mk index 4d5793b206..a78c63dd7e 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -2301,7 +2301,13 @@ dist_patch_DATA = \ %D%/packages/patches/seq24-rename-mutex.patch \ %D%/packages/patches/libsequoia-fix-ffi-Makefile.patch \ %D%/packages/patches/libsequoia-remove-store.patch \ - %D%/packages/patches/shakespeare-spl-fix-grammar.patch \ + %D%/packages/patches/libsoup-auth-digest-fix-crash.patch \ + %D%/packages/patches/libsoup-deadlock-in-add_listener_in_thread.patch \ + %D%/packages/patches/libsoup-fix-merge-of-ranges.patch \ + %D%/packages/patches/libsoup-memory-leak-in-soup_form_decode.patch \ + %D%/packages/patches/libsoup-multipart-bounds-check.patch \ + %D%/packages/patches/libsoup-use-libdl-instead-of-gmodule.patch \ + %D%/packages/patches/shakespeare-spl-fix-grammar.patch \ %D%/packages/patches/shared-mime-info-xdgmime-path.patch \ %D%/packages/patches/sharutils-CVE-2018-1000097.patch \ %D%/packages/patches/sipwitch-fix-build-with-exosip5.patch \ diff --git a/gnu/packages/gnome.scm b/gnu/packages/gnome.scm index e6e1718731..2f30f6925e 100644 --- a/gnu/packages/gnome.scm +++ b/gnu/packages/gnome.scm @@ -5288,7 +5288,15 @@ as OpenStreetMap, OpenCycleMap, OpenAerialMap and Maps.") "libsoup-" version ".tar.xz")) (sha256 (base32 - "0d52mnvvsvwpc3scjva5fbvns8f8ijyswgjwjhbr151ymid7d4b8")))) + "0d52mnvvsvwpc3scjva5fbvns8f8ijyswgjwjhbr151ymid7d4b8")) + (patches + (search-patches + "libsoup-auth-digest-fix-crash.patch" + "libsoup-deadlock-in-add_listener_in_thread.patch" + "libsoup-fix-merge-of-ranges.patch" + "libsoup-memory-leak-in-soup_form_decode.patch" + "libsoup-multipart-bounds-check.patch" + "libsoup-use-libdl-instead-of-gmodule.patch")))) (build-system meson-build-system) (arguments (list @@ -5312,21 +5320,21 @@ as OpenStreetMap, OpenCycleMap, OpenAerialMap and Maps.") (substitute* "tests/hsts-db-test.c" ((".*/hsts-db/subdomains.*") ""))))))) (native-inputs - (list `(,glib "bin") ;for glib-mkenums + (list `(,glib "bin") ;for glib-mkenums gobject-introspection pkg-config python-wrapper vala curl - gnutls ;for 'certtool' + gnutls ;for 'certtool' httpd/pinned)) (propagated-inputs ;; libsoup-3.0.pc refers to all of these (except where otherwise noted) (list brotli glib - glib-networking ; for GIO runtime modules + glib-networking ; for GIO runtime modules libpsl - nghttp2 ;for pkg-config + nghttp2 ;for pkg-config `(,nghttp2 "lib") libxml2 mit-krb5 diff --git a/gnu/packages/patches/libsoup-auth-digest-fix-crash.patch b/gnu/packages/patches/libsoup-auth-digest-fix-crash.patch new file mode 100644 index 0000000000..7b147338a2 --- /dev/null +++ b/gnu/packages/patches/libsoup-auth-digest-fix-crash.patch @@ -0,0 +1,31 @@ +From: Michael Catanzaro <mcatanzaro@redhat.com> +Date: Thu, 8 May 2025 09:27:01 -0500 +Subject: auth-digest: fix crash in soup_auth_digest_get_protection_space() + +We need to validate the Domain parameter in the WWW-Authenticate header. + +Unfortunately this crash only occurs when listening on default ports 80 +and 443, so there's no good way to test for this. The test would require +running as root. + +Origin: upstream, 3.7.0, commit:e64c221f9c7d09b48b610c5626b3b8c400f0907c +Bug: https://gitlab.gnome.org/GNOME/libsoup/-/issues/440 +Bug-CVE: https://security-tracker.debian.org/tracker/CVE-2025-4476 +Bug-Debian: https://bugs.debian.org/1105887 +--- + libsoup/auth/soup-auth-digest.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libsoup/auth/soup-auth-digest.c b/libsoup/auth/soup-auth-digest.c +index d8bb291..292f204 100644 +--- a/libsoup/auth/soup-auth-digest.c ++++ b/libsoup/auth/soup-auth-digest.c +@@ -220,7 +220,7 @@ soup_auth_digest_get_protection_space (SoupAuth *auth, GUri *source_uri) + if (uri && + g_strcmp0 (g_uri_get_scheme (uri), g_uri_get_scheme (source_uri)) == 0 && + g_uri_get_port (uri) == g_uri_get_port (source_uri) && +- !strcmp (g_uri_get_host (uri), g_uri_get_host (source_uri))) ++ !g_strcmp0 (g_uri_get_host (uri), g_uri_get_host (source_uri))) + dir = g_strdup (g_uri_get_path (uri)); + else + dir = NULL; diff --git a/gnu/packages/patches/libsoup-deadlock-in-add_listener_in_thread.patch b/gnu/packages/patches/libsoup-deadlock-in-add_listener_in_thread.patch new file mode 100644 index 0000000000..1e3fa161bc --- /dev/null +++ b/gnu/packages/patches/libsoup-deadlock-in-add_listener_in_thread.patch @@ -0,0 +1,42 @@ +From: Michael Catanzaro <mcatanzaro@redhat.com> +Date: Wed, 30 Apr 2025 14:13:41 -0500 +Subject: test-utils: fix deadlock in add_listener_in_thread() + +The mutex is locked in the wrong place here. + +Hopefully fixes #379 + +Origin: upstream, 3.7.0, commit:3c0cee2cfddb9ba31b30421f2b3cdd3c5a255e99 +Bug: https://gitlab.gnome.org/GNOME/libsoup/-/issues/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 62f0b83..6fb09b6 100644 +--- a/tests/test-utils.c ++++ b/tests/test-utils.c +@@ -627,9 +627,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); + +@@ -661,9 +663,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); + diff --git a/gnu/packages/patches/libsoup-fix-merge-of-ranges.patch b/gnu/packages/patches/libsoup-fix-merge-of-ranges.patch new file mode 100644 index 0000000000..5cc7325a5d --- /dev/null +++ b/gnu/packages/patches/libsoup-fix-merge-of-ranges.patch @@ -0,0 +1,192 @@ +From: Milan Crha <mcrha@redhat.com> +Date: Tue, 15 Apr 2025 12:17:39 +0200 +Subject: soup-message-headers: Correct merge of ranges + +It had been skipping every second range, which generated an array +of a lot of insane ranges, causing large memory usage by the server. + +Origin: upstream, 3.7.0, commit:9bb92f7a685e31e10e9e8221d0342280432ce836 +Bug: https://gitlab.gnome.org/GNOME/libsoup/-/issues/428 +Bug-CVE: https://security-tracker.debian.org/tracker/CVE-2025-32907 +Bug-Debian: https://bugs.debian.org/1103264 +--- + libsoup/soup-message-headers.c | 1 + + tests/meson.build | 1 + + tests/server-mem-limit-test.c | 144 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 146 insertions(+) + create mode 100644 tests/server-mem-limit-test.c + +diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c +index ee7a3cb..f101d4b 100644 +--- a/libsoup/soup-message-headers.c ++++ b/libsoup/soup-message-headers.c +@@ -1244,6 +1244,7 @@ soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs, + if (cur->start <= prev->end) { + prev->end = MAX (prev->end, cur->end); + g_array_remove_index (array, i); ++ i--; + } + } + } +diff --git a/tests/meson.build b/tests/meson.build +index cf4ddbd..68e6d01 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -102,6 +102,7 @@ tests = [ + {'name': 'samesite'}, + {'name': 'session'}, + {'name': 'server-auth'}, ++ {'name': 'server-mem-limit'}, + {'name': 'server'}, + {'name': 'sniffing', + 'depends': [test_resources], +diff --git a/tests/server-mem-limit-test.c b/tests/server-mem-limit-test.c +new file mode 100644 +index 0000000..98f1c40 +--- /dev/null ++++ b/tests/server-mem-limit-test.c +@@ -0,0 +1,144 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ ++/* ++ * Copyright (C) 2025 Red Hat <www.redhat.com> ++ */ ++ ++#include "test-utils.h" ++ ++#include <sys/resource.h> ++ ++/* ++ This test limits memory usage to trigger too large buffer allocation crash. ++ As restoring the limits back to what it was does not always work, it's split ++ out of the server-test.c test with copied minimal server code. ++ */ ++ ++typedef struct { ++ SoupServer *server; ++ GUri *base_uri, *ssl_base_uri; ++ GSList *handlers; ++} ServerData; ++ ++static void ++server_setup_nohandler (ServerData *sd, gconstpointer test_data) ++{ ++ sd->server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); ++ sd->base_uri = soup_test_server_get_uri (sd->server, "http", NULL); ++ if (tls_available) ++ sd->ssl_base_uri = soup_test_server_get_uri (sd->server, "https", NULL); ++} ++ ++static void ++server_add_handler (ServerData *sd, ++ const char *path, ++ SoupServerCallback callback, ++ gpointer user_data, ++ GDestroyNotify destroy) ++{ ++ soup_server_add_handler (sd->server, path, callback, user_data, destroy); ++ sd->handlers = g_slist_prepend (sd->handlers, g_strdup (path)); ++} ++ ++static void ++server_setup (ServerData *sd, gconstpointer test_data) ++{ ++ server_setup_nohandler (sd, test_data); ++} ++ ++static void ++server_teardown (ServerData *sd, gconstpointer test_data) ++{ ++ GSList *iter; ++ ++ for (iter = sd->handlers; iter; iter = iter->next) ++ soup_server_remove_handler (sd->server, iter->data); ++ g_slist_free_full (sd->handlers, g_free); ++ ++ g_clear_pointer (&sd->server, soup_test_server_quit_unref); ++ g_clear_pointer (&sd->base_uri, g_uri_unref); ++ g_clear_pointer (&sd->ssl_base_uri, g_uri_unref); ++} ++ ++static void ++server_file_callback (SoupServer *server, ++ SoupServerMessage *msg, ++ const char *path, ++ GHashTable *query, ++ gpointer data) ++{ ++ void *mem; ++ ++ g_assert_cmpstr (path, ==, "/file"); ++ g_assert_cmpstr (soup_server_message_get_method (msg), ==, SOUP_METHOD_GET); ++ ++ mem = g_malloc0 (sizeof (char) * 1024 * 1024); ++ /* fedora-scan CI claims a warning about possibly leaked `mem` variable, thus use ++ the copy and free it explicitly, to workaround the false positive; the g_steal_pointer() ++ did not help for the malloc-ed memory */ ++ soup_server_message_set_response (msg, "application/octet-stream", SOUP_MEMORY_COPY, mem, sizeof (char) * 1024 *1024); ++ soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); ++ g_free (mem); ++} ++ ++static void ++do_ranges_overlaps_test (ServerData *sd, gconstpointer test_data) ++{ ++ SoupSession *session; ++ SoupMessage *msg; ++ GString *range; ++ GUri *uri; ++ const char *chunk = ",0,0,0,0,0,0,0,0,0,0,0"; ++ ++ g_test_bug ("428"); ++ ++ #ifdef G_OS_WIN32 ++ g_test_skip ("Cannot run under windows"); ++ return; ++ #endif ++ ++ range = g_string_sized_new (99 * 1024); ++ g_string_append (range, "bytes=1024"); ++ while (range->len < 99 * 1024) ++ g_string_append (range, chunk); ++ ++ session = soup_test_session_new (NULL); ++ server_add_handler (sd, "/file", server_file_callback, NULL, NULL); ++ ++ uri = g_uri_parse_relative (sd->base_uri, "/file", SOUP_HTTP_URI_FLAGS, NULL); ++ ++ msg = soup_message_new_from_uri ("GET", uri); ++ soup_message_headers_append (soup_message_get_request_headers (msg), "Range", range->str); ++ ++ soup_test_session_send_message (session, msg); ++ ++ soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT); ++ ++ g_object_unref (msg); ++ ++ g_string_free (range, TRUE); ++ g_uri_unref (uri); ++ ++ soup_test_session_abort_unref (session); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int ret; ++ ++ test_init (argc, argv, NULL); ++ ++ #ifndef G_OS_WIN32 ++ struct rlimit new_rlimit = { 1024 * 1024 * 64, 1024 * 1024 * 64 }; ++ /* limit memory usage, to trigger too large memory allocation abort */ ++ g_assert_cmpint (setrlimit (RLIMIT_DATA, &new_rlimit), ==, 0); ++ #endif ++ ++ g_test_add ("/server-mem/range-overlaps", ServerData, NULL, ++ server_setup, do_ranges_overlaps_test, server_teardown); ++ ++ ret = g_test_run (); ++ ++ test_cleanup (); ++ return ret; ++} diff --git a/gnu/packages/patches/libsoup-memory-leak-in-soup_form_decode.patch b/gnu/packages/patches/libsoup-memory-leak-in-soup_form_decode.patch new file mode 100644 index 0000000000..ccba980ab7 --- /dev/null +++ b/gnu/packages/patches/libsoup-memory-leak-in-soup_form_decode.patch @@ -0,0 +1,101 @@ +From: Milan Crha <mcrha@redhat.com> +Date: Tue, 13 May 2025 10:38:49 +0200 +Subject: soup-form: Fix a possible memory leak in + soup_form_decode_multipart() + +The output variables can be set multiple times, when there are multiparts +with the same name, thus first clear any previously value and only then +assign a new value. + +Origin: upstream, 3.7.0, commit:66b5c5be947062df9caf7025b56ee1de32aee3ac +Bug: https://gitlab.gnome.org/GNOME/libsoup/-/issues/446 +--- + libsoup/soup-form.c | 12 +++++++++--- + tests/forms-test.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 50 insertions(+), 3 deletions(-) + +diff --git a/libsoup/soup-form.c b/libsoup/soup-form.c +index 2eb5d57..98130c8 100644 +--- a/libsoup/soup-form.c ++++ b/libsoup/soup-form.c +@@ -168,12 +168,18 @@ soup_form_decode_multipart (SoupMultipart *multipart, + } + + if (file_control_name && !strcmp (name, file_control_name)) { +- if (filename) ++ if (filename) { ++ g_free (*filename); + *filename = g_strdup (g_hash_table_lookup (params, "filename")); +- if (content_type) ++ } ++ if (content_type) { ++ g_free (*content_type); + *content_type = g_strdup (soup_message_headers_get_content_type (part_headers, NULL)); +- if (file) ++ } ++ if (file) { ++ g_clear_pointer (file, g_bytes_unref); + *file = g_bytes_ref (part_body); ++ } + } else { + g_hash_table_insert (form_data_set, + g_strdup (name), +diff --git a/tests/forms-test.c b/tests/forms-test.c +index 1002374..183900f 100644 +--- a/tests/forms-test.c ++++ b/tests/forms-test.c +@@ -485,6 +485,46 @@ md5_callback (SoupServer *server, + soup_server_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED, NULL); + } + ++static void ++do_form_decode_multipart_test (void) ++{ ++ SoupMultipart *multipart = soup_multipart_new ("multipart/form-data"); ++ const char *file_control_name = "uploaded_file"; ++ char *content_type = NULL; ++ char *filename = NULL; ++ GBytes *file = NULL; ++ GHashTable *result; ++ int part; ++ ++ for (part = 0; part < 2; part++) { ++ SoupMessageHeaders *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); ++ GHashTable *params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); ++ GBytes *body = g_bytes_new (NULL, 0); ++ ++ g_hash_table_insert (params, g_strdup ("name"), g_strdup (file_control_name)); ++ g_hash_table_insert (params, g_strdup ("filename"), g_strdup (file_control_name)); ++ soup_message_headers_set_content_disposition (headers, "form-data", params); ++ soup_message_headers_set_content_type (headers, "text/x-form", NULL); ++ soup_multipart_append_part (multipart, headers, body); ++ ++ soup_message_headers_unref (headers); ++ g_hash_table_destroy (params); ++ g_bytes_unref (body); ++ } ++ ++ /* this would leak memory of the output variables, due to two parts having the same 'file_control_name' */ ++ result = soup_form_decode_multipart (multipart, file_control_name, &filename, &content_type, &file); ++ g_assert_nonnull (result); ++ g_assert_cmpstr (content_type, ==, "text/x-form"); ++ g_assert_cmpstr (filename, ==, file_control_name); ++ g_assert_nonnull (file); ++ ++ g_hash_table_destroy (result); ++ g_free (content_type); ++ g_free (filename); ++ g_bytes_unref (file); ++} ++ + static gboolean run_tests = TRUE; + + static GOptionEntry no_test_entry[] = { +@@ -525,6 +565,7 @@ main (int argc, char **argv) + g_uri_unref (uri); + + g_test_add_func ("/forms/decode", do_form_decode_test); ++ g_test_add_func ("/forms/decodemultipart", do_form_decode_multipart_test); + + ret = g_test_run (); + } else { diff --git a/gnu/packages/patches/libsoup-multipart-bounds-check.patch b/gnu/packages/patches/libsoup-multipart-bounds-check.patch new file mode 100644 index 0000000000..0bafe09206 --- /dev/null +++ b/gnu/packages/patches/libsoup-multipart-bounds-check.patch @@ -0,0 +1,106 @@ +From: Milan Crha <mcrha@redhat.com> +Date: Tue, 15 Apr 2025 09:03:00 +0200 +Subject: 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. + +Origin: upstream, 3.7.0, commit:5bfcf8157597f2d327050114fb37ff600004dbcf +Bug: https://gitlab.gnome.org/GNOME/libsoup/-/issues/436 +Bug-CVE: https://security-tracker.debian.org/tracker/CVE-2025-32914 +Bug-Debian: https://bugs.debian.org/1103267 +--- + 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 2421c91..102ce37 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 2c0e7e9..f5b9868 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 (); + diff --git a/gnu/packages/patches/libsoup-use-libdl-instead-of-gmodule.patch b/gnu/packages/patches/libsoup-use-libdl-instead-of-gmodule.patch new file mode 100644 index 0000000000..3b6608cb04 --- /dev/null +++ b/gnu/packages/patches/libsoup-use-libdl-instead-of-gmodule.patch @@ -0,0 +1,70 @@ +From: Fabio Manganiello <fabio@manganiello.tech> +Date: Tue, 15 Jul 2025 15:41:47 +0200 +Subject: soup-init: Use libdl instead of gmodule in `soup2_is_loaded` check + +Calling `g_module_open` in the library constructor can cause deadlocks +when libsoup is used with other libraries that also contend for GLib +mutexes. `dlopen` should be used instead. + +Co-authored-by: Nirbheek Chauhan <nirbheek@centricular.com> +Bug: https://gitlab.gnome.org/GNOME/libsoup/-/issues/463 +Bug: https://gitlab.gnome.org/GNOME/glib/-/issues/1443 +Bug-Debian: https://bugs.debian.org/1109685 +Origin: https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/475 +Applied-upstream: 3.7.0, commit:1296cbf983f036f20262c453926dff77e1d6a852 +Applied-upstream: 3.6.6, commit:2316e56a5502ac4c41ef4ff56a3266e680aca129 +--- + libsoup/soup-init.c | 28 +++++++++++++++++----------- + 1 file changed, 17 insertions(+), 11 deletions(-) + +diff --git a/libsoup/soup-init.c b/libsoup/soup-init.c +index 8a33c77..3392e8e 100644 +--- a/libsoup/soup-init.c ++++ b/libsoup/soup-init.c +@@ -10,7 +10,6 @@ + #endif + + #include <glib/gi18n-lib.h> +-#include <gmodule.h> + #include "gconstructor.h" + + #ifdef G_OS_WIN32 +@@ -18,21 +17,28 @@ + #include <windows.h> + + HMODULE soup_dll; ++#else ++#include <dlfcn.h> + #endif + + static gboolean + soup2_is_loaded (void) + { +- GModule *module = g_module_open (NULL, 0); +- gpointer func; +- gboolean result = FALSE; +- +- if (g_module_symbol (module, "soup_uri_new", &func)) +- result = TRUE; +- +- g_module_close (module); +- +- return result; ++ gboolean result = FALSE; ++ ++ /* Skip on PE/COFF, as it doesn't have a flat symbol namespace */ ++#ifndef G_OS_WIN32 ++ gpointer handle; ++ gpointer func; ++ ++ handle = dlopen (NULL, RTLD_LAZY | RTLD_GLOBAL); ++ if (handle != NULL) { ++ func = dlsym (handle, "soup_uri_new"); ++ result = (func != NULL); ++ dlclose (handle); ++ } ++#endif ++ return result; + } + + static void |