Version in base suite: 2.84.4-3~deb13u3 Base version: glib2.0_2.84.4-3~deb13u3 Target version: glib2.0_2.84.4-3~deb13u4 Base file: /srv/ftp-master.debian.org/ftp/pool/main/g/glib2.0/glib2.0_2.84.4-3~deb13u3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/g/glib2.0/glib2.0_2.84.4-3~deb13u4.dsc changelog | 83 ++- patches/fuzzing-Add-a-fuzz-test-for-g_dbus_node_info_new_for_xml.patch | 68 ++ patches/gdatetime-Add-missing-range-validation-to-g_date_time_add.patch | 66 ++ patches/gdatetime-Factor-out-a-couple-of-magic-constants.patch | 55 ++ patches/gdbusauth-Limit-length-of-lines-read-from-client.patch | 265 ++++++++++ patches/gdbusauthmechanism-Expose-client-reject-reason-as-a-new-v.patch | 189 +++++++ patches/gdbusauthmechanismsha1-Improve-validation-of-cookie-ID.patch | 56 ++ patches/gdbusauthmechanismsha1-Validate-cookie-context.patch | 77 ++ patches/gdbusintrospection-Add-some-assertions-before-array-deref.patch | 90 +++ patches/gdbusintrospection-Fix-XML-parser-state-handling-for-node.patch | 89 +++ patches/gdbusmessage-Fix-types-of-integer-arithmetic-in-message-l.patch | 141 +++++ patches/giochannel-Fix-memcmp-off-the-end-of-the-buffer-with-long.patch | 131 ++++ patches/gkeyfile-Fix-a-one-byte-heap-under-read-with-g_key_file_g.patch | 102 +++ patches/gmarkup-Fix-potential-one-byte-overread-in-g_markup_escap.patch | 96 +++ patches/gregex-Fix-case-changing-substitutions-with-G_REGEX_RAW.patch | 221 ++++++++ patches/gregex-Fix-use-of-wrong-option-flags-set-for-checking-for.patch | 90 +++ patches/gregex-Rename-the-compile_opts-members-to-clarify-their-t.patch | 140 +++++ patches/gvariant-Fix-an-off-by-one-error-in-an-offset-comparison.patch | 106 ++++ patches/series | 19 patches/tests-Add-a-unit-test-for-GDBusAuthMechanismSha1-cookie-c.patch | 211 +++++++ patches/tests-Improve-D-Bus-introspection-test-paths.patch | 34 + salsa-ci.yml | 3 tests/manual/.gitignore | 1 23 files changed, 2325 insertions(+), 8 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp10vuf24q/glib2.0_2.84.4-3~deb13u3.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp10vuf24q/glib2.0_2.84.4-3~deb13u4.dsc: no acceptable signature found diff -Nru glib2.0-2.84.4/debian/changelog glib2.0-2.84.4/debian/changelog --- glib2.0-2.84.4/debian/changelog 2026-02-16 08:11:04.000000000 +0000 +++ glib2.0-2.84.4/debian/changelog 2026-08-07 09:33:15.000000000 +0000 @@ -1,16 +1,83 @@ +glib2.0 (2.84.4-3~deb13u4) trixie; urgency=medium + + * Edit previous changelog entry to correlate CVE fixes with upstream + bug numbers and releases + * Add patches from upstream 2.86.5 to fix out-of-bounds accesses: + - d/p/gvariant-Fix-an-off-by-one-error-in-an-offset-comparison.patch: + Fix a potential out of bounds read by 1 byte + (CVE-2026-58010, glib#3915 upstream) + - d/p/gmarkup-Fix-potential-one-byte-overread-in-g_markup_escap.patch: + Fix a potential out of bounds read by 1 byte when escaping text that + is not valid UTF-8 + (not considered to be a vulnerability, glib#3916 upstream) + - d/p/gdatetime-Factor-out-a-couple-of-magic-constants.patch, + d/p/gdatetime-Add-missing-range-validation-to-g_date_time_add.patch: + Fix an out of bounds read by up to 2 bytes after parsing an + out-of-range date + (CVE-2026-58011, glib#3917 upstream) + - d/p/gregex-Fix-case-changing-substitutions-with-G_REGEX_RAW.patch: + Fix a potential buffer overflow when changing the case of an incomplete + UTF-8 sequence while using G_REGEX_RAW + (CVE-2026-58012, glib#3918 upstream) + - d/p/gregex-Fix-use-of-wrong-option-flags-set-for-checking-for.patch, + d/p/gregex-Rename-the-compile_opts-members-to-clarify-their-t.patch: + Fix an out-of-bounds read when g_regex_split_full() acts on + invalid UTF-8 + (not considered to be a vulnerability, glib#3919 upstream) + * Add patches from upstream 2.88.1 to fix several issues that were reported + as potential security vulnerabilities: + - d/p/giochannel-Fix-memcmp-off-the-end-of-the-buffer-with-long.patch: + Fix out-of-bounds read if a GIOChannel is configured with a long + line-terminator + (CVE-2026-58013, glib#3825 upstream) + - d/p/gkeyfile-Fix-a-one-byte-heap-under-read-with-g_key_file_g.patch: + Fix out-of-bounds read if a list of locale-dependent strings in a + GKeyFile is empty + (CVE-2026-58014, glib#3930 upstream) + - d/p/gdbusmessage-Fix-types-of-integer-arithmetic-in-message-l.patch: + Fix an integer overflow that could lead to accepting overly large + messages on peer-to-peer D-Bus connections + (no CVE ID, glib#3933 upstream) + - d/p/gdbusauthmechanismsha1-Validate-cookie-context.patch, + d/p/gdbusauthmechanismsha1-Improve-validation-of-cookie-ID.patch, + d/p/gdbusauthmechanism-Expose-client-reject-reason-as-a-new-v.patch, + d/p/tests-Add-a-unit-test-for-GDBusAuthMechanismSha1-cookie-c.patch: + Prevent path traversal and file-content disclosure if a D-Bus client + connects to a malicious peer-to-peer D-Bus server + (CVE-2026-58015, glib#3931 upstream) + * Add patch from upstream 2.88.3 fixing a possible denial of service: + - d/p/gdbusauth-Limit-length-of-lines-read-from-client.patch: + Fix resource exhaustion if a malicious client can contact a + GDBusServer + (CVE-2026-15588, glib#3985 upstream, Closes: #1142835) + * Add patches from upstream 2.89.0 to harden D-Bus introspection parsing + - d/p/gdbusintrospection-Add-some-assertions-before-array-deref.patch, + d/p/tests-Improve-D-Bus-introspection-test-paths.patch, + d/p/gdbusintrospection-Fix-XML-parser-state-handling-for-node.patch, + d/p/fuzzing-Add-a-fuzz-test-for-g_dbus_node_info_new_for_xml.patch: + Avoid a possible integer underflow if parsing malformed D-Bus + introspection XML sent by a malicious service + (glib#3932 upstream, CVE-2026-58016, Closes: #1141316) + * d/salsa-ci.yml: Disable uscan job as not relevant to this stable branch + + -- Simon McVittie Fri, 07 Aug 2026 10:33:15 +0100 + glib2.0 (2.84.4-3~deb13u3) trixie; urgency=medium * Non-maintainer upload by the LTS Security Team. * Add patch to fix timezone handling with Debian & Ubuntu's symlinks - (Closes: #1119919) (LP: #2130378) + (glib#3816 upstream, Closes: #1119919) (LP: #2130378) + * Add patches from 2.86.4 upstream to fix more integer overflows + - CVE-2026-1484: Integer overflow in base64 encoding can cause memory + corruption. (glib#3870 upstream, Closes: #1126551) + - CVE-2026-1485: Buffer underflow vulnerability in content type parsing + caused by (signed) integer wrap for large inputs. + (glib#3871 upstream, Closes: #1126550) + - CVE-2026-1489: Integer overflow in unicode conversion + can lead to memory corruption. + (glib#3872 upstream, Closes: #1126549) * CVE-2026-0988: Missing input validation in g_buffered_input_stream_peek - (Closes: #1125752) - * CVE-2026-1484: Integer overflow in base64 encoding can cause memory - corruption. (Closes: #1126551) - * CVE-2026-1485: Buffer underflow vulnerability in content type parsing - caused by (signed) integer wrap for large inputs. (Closes: #1126550) - * CVE-2026-1489: Integer overflow in unicode conversion - can lead to memory corruption. (Closes: #1126549) + (glib#3851 upstream, Closes: #1125752) -- Andreas Henriksson Mon, 16 Feb 2026 09:11:04 +0100 diff -Nru glib2.0-2.84.4/debian/patches/fuzzing-Add-a-fuzz-test-for-g_dbus_node_info_new_for_xml.patch glib2.0-2.84.4/debian/patches/fuzzing-Add-a-fuzz-test-for-g_dbus_node_info_new_for_xml.patch --- glib2.0-2.84.4/debian/patches/fuzzing-Add-a-fuzz-test-for-g_dbus_node_info_new_for_xml.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/fuzzing-Add-a-fuzz-test-for-g_dbus_node_info_new_for_xml.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,68 @@ +From: Philip Withnall +Date: Sun, 19 Apr 2026 12:16:03 +0100 +Subject: fuzzing: Add a fuzz test for g_dbus_node_info_new_for_xml() + +Signed-off-by: Philip Withnall + +Origin: upstream, 2.89.0, commit:4b3e3b6b69ddbca73a888c53bd98ef6916a84490 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3932 +Bug-Debian: https://bugs.debian.org/1141316 +--- + ...lename_to_uri.c => fuzz_dbus_node_info_new_for_xml.c} | 16 +++++++++------- + fuzzing/meson.build | 1 + + 2 files changed, 10 insertions(+), 7 deletions(-) + copy fuzzing/{fuzz_filename_to_uri.c => fuzz_dbus_node_info_new_for_xml.c} (67%) + +diff --git a/fuzzing/fuzz_filename_to_uri.c b/fuzzing/fuzz_dbus_node_info_new_for_xml.c +similarity index 67% +copy from fuzzing/fuzz_filename_to_uri.c +copy to fuzzing/fuzz_dbus_node_info_new_for_xml.c +index acb3192..e16c824 100644 +--- a/fuzzing/fuzz_filename_to_uri.c ++++ b/fuzzing/fuzz_dbus_node_info_new_for_xml.c +@@ -1,5 +1,5 @@ + /* +- * Copyright 2025 GNOME Foundation, Inc. ++ * Copyright 2026 Philip Withnall + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * +@@ -22,18 +22,20 @@ + int + LLVMFuzzerTestOneInput (const unsigned char *data, size_t size) + { +- unsigned char *nul_terminated_data = NULL; +- char *uri = NULL; ++ char *nul_terminated_data = NULL; ++ GDBusNodeInfo *node = NULL; + GError *local_error = NULL; + + fuzz_set_logging_func (); + +- /* ignore @size (g_filename_to_uri() doesn’t support it); ensure @data is nul-terminated */ +- nul_terminated_data = (unsigned char *) g_strndup ((const char *) data, size); +- uri = g_filename_to_uri ((const char *) nul_terminated_data, NULL, &local_error); ++ /* ignore @size (g_dbus_node_info_new_for_xml() doesn’t support it); ensure @data is nul-terminated */ ++ nul_terminated_data = g_strndup ((const gchar *) data, size); ++ node = g_dbus_node_info_new_for_xml (nul_terminated_data, &local_error); + g_free (nul_terminated_data); + +- g_free (uri); ++ g_assert ((node == NULL) == (local_error != NULL)); ++ ++ g_clear_pointer (&node, g_dbus_node_info_unref); + g_clear_error (&local_error); + + return 0; +diff --git a/fuzzing/meson.build b/fuzzing/meson.build +index accff0a..5eb2eaa 100644 +--- a/fuzzing/meson.build ++++ b/fuzzing/meson.build +@@ -25,6 +25,7 @@ fuzz_targets = [ + 'fuzz_date_parse', + 'fuzz_date_time_new_from_iso8601', + 'fuzz_dbus_message', ++ 'fuzz_dbus_node_info_new_for_xml', + 'fuzz_filename_from_uri', + 'fuzz_filename_to_uri', + 'fuzz_get_locale_variants', diff -Nru glib2.0-2.84.4/debian/patches/gdatetime-Add-missing-range-validation-to-g_date_time_add.patch glib2.0-2.84.4/debian/patches/gdatetime-Add-missing-range-validation-to-g_date_time_add.patch --- glib2.0-2.84.4/debian/patches/gdatetime-Add-missing-range-validation-to-g_date_time_add.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdatetime-Add-missing-range-validation-to-g_date_time_add.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,66 @@ +From: Philip Withnall +Date: Sun, 29 Mar 2026 23:46:17 +0100 +Subject: gdatetime: Add missing range validation to g_date_time_add_full() +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +Otherwise it’s possible to create a non-`NULL` but invalid `GDateTime`, +which breaks all kinds of internal assumptions. + +Spotted by linhlhq as #YWH-PGM9867-191. Thanks to them for providing a +suggested fix and a test case, which I have adapted and validated. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.86.5, commit:ae27363f025ffc131e2d75ee88a5cd8320dffe3b +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3917 +Bug-CVE: CVE-2026-58011 +--- + glib/gdatetime.c | 4 +++- + glib/tests/gdatetime.c | 18 ++++++++++++++++++ + 2 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/glib/gdatetime.c b/glib/gdatetime.c +index 5537820..f472854 100644 +--- a/glib/gdatetime.c ++++ b/glib/gdatetime.c +@@ -2078,7 +2078,9 @@ g_date_time_add_full (GDateTime *datetime, + new->days = full_time / USEC_PER_DAY; + new->usec = full_time % USEC_PER_DAY; + +- /* XXX validate */ ++ /* Validate it’s still in the range 0001-01-01 to 9999-12-31 */ ++ if (new->days < MIN_DAYS || new->days > MAX_DAYS) ++ g_clear_pointer (&new, g_date_time_unref); + + return new; + } +diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c +index b061c91..8d0e55d 100644 +--- a/glib/tests/gdatetime.c ++++ b/glib/tests/gdatetime.c +@@ -1141,6 +1141,24 @@ test_GDateTime_add_full (void) + TEST_ADD_FULL (2010, 8, 25, 22, 45, 0, + 0, 1, 6, 1, 25, 0, + 2010, 10, 2, 0, 10, 0); ++ ++#define TEST_ADD_FULL_ERROR(y,m,d,h,mi,s,ay,am,ad,ah,ami,as) G_STMT_START { \ ++ GDateTime *dt; \ ++ dt = g_date_time_new_utc (y, m, d, h, mi, s); \ ++ g_assert_null (g_date_time_add_full (dt, ay, am, ad, ah, ami, as)); \ ++ g_date_time_unref (dt); \ ++} G_STMT_END ++ ++ TEST_ADD_FULL_ERROR ( 1, 12, 1, 0, 0, 0, ++ -1, 0, 0, 0, 0, 0); ++ TEST_ADD_FULL_ERROR ( 1, 12, 1, 0, 0, 0, ++ 10000, 0, 0, 0, 0, 0); ++ TEST_ADD_FULL_ERROR ( 9999, 12, 1, 0, 0, 0, ++ -10000, 0, 0, 0, 0, 0); ++ TEST_ADD_FULL_ERROR ( 1, 12, 1, 0, 0, 0, ++ 0, 0, 3660001, 0, 0, 0); ++ TEST_ADD_FULL_ERROR ( 9999, 12, 1, 0, 0, 0, ++ 0, 0, -3660001, 0, 0, 0); + } + + static void diff -Nru glib2.0-2.84.4/debian/patches/gdatetime-Factor-out-a-couple-of-magic-constants.patch glib2.0-2.84.4/debian/patches/gdatetime-Factor-out-a-couple-of-magic-constants.patch --- glib2.0-2.84.4/debian/patches/gdatetime-Factor-out-a-couple-of-magic-constants.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdatetime-Factor-out-a-couple-of-magic-constants.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,55 @@ +From: Philip Withnall +Date: Sun, 29 Mar 2026 23:19:47 +0100 +Subject: gdatetime: Factor out a couple of magic constants + +This introduces no functional changes, it just makes the code a little +clearer. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.86.5, commit:2521a538a5ff810d6d18ff3f5b105ee55419d694 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3917 +--- + glib/gdatetime.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/glib/gdatetime.c b/glib/gdatetime.c +index 906ba32..5537820 100644 +--- a/glib/gdatetime.c ++++ b/glib/gdatetime.c +@@ -99,7 +99,7 @@ struct _GDateTime + gint interval; + + /* 1 is 0001-01-01 in Proleptic Gregorian */ +- gint32 days; ++ gint32 days; /* in range [MIN_DAYS, MAX_DAYS] */ + + gint ref_count; /* (atomic) */ + }; +@@ -141,6 +141,9 @@ struct _GDateTime + #define JULIAN_YEAR(d) ((d)->julian / 365.25) + #define DAYS_PER_PERIOD (G_GINT64_CONSTANT (2914695)) + ++#define MIN_DAYS 1 /* the days count for 0001-01-01 in Proleptic Gregorian */ ++#define MAX_DAYS 3652059 /* the days count for 9999-12-31 in Proleptic Gregorian */ ++ + static const guint16 days_in_months[2][13] = + { + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, +@@ -775,7 +778,7 @@ g_date_time_from_instant (GTimeZone *tz, + datetime->days = instant / USEC_PER_DAY; + datetime->usec = instant % USEC_PER_DAY; + +- if (datetime->days < 1 || 3652059 < datetime->days) ++ if (datetime->days < MIN_DAYS || datetime->days > MAX_DAYS) + { + g_date_time_unref (datetime); + datetime = NULL; +@@ -811,7 +814,7 @@ g_date_time_deal_with_date_change (GDateTime *datetime) + gint64 full_time; + gint64 usec; + +- if (datetime->days < 1 || datetime->days > 3652059) ++ if (datetime->days < MIN_DAYS || datetime->days > MAX_DAYS) + return FALSE; + + was_dst = g_time_zone_is_dst (datetime->tz, datetime->interval); diff -Nru glib2.0-2.84.4/debian/patches/gdbusauth-Limit-length-of-lines-read-from-client.patch glib2.0-2.84.4/debian/patches/gdbusauth-Limit-length-of-lines-read-from-client.patch --- glib2.0-2.84.4/debian/patches/gdbusauth-Limit-length-of-lines-read-from-client.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdbusauth-Limit-length-of-lines-read-from-client.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,265 @@ +From: Philip Withnall +Date: Sat, 4 Jul 2026 18:13:08 +0100 +Subject: gdbusauth: Limit length of lines read from client +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +The client isn’t trusted at this point, and there was previously nothing +limiting how long a line `GDBusAuth` would read. So an untrusted client +could exhaust the server’s memory by sending anything except `\r\n`. + +Fix that by applying a reasonably length limit when reading a line, and +add a unit test. + +Spotted by Gitee Codepecker Lab. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.88.3, commit:4235f7b42ba51d6fdb4abd7c4276031802f39834 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3985 +Bug-CVE: CVE-2026-15588 +Bug-Debian: https://bugs.debian.org/1142835 +--- + gio/gdbusauth.c | 44 ++++++++++++++++++ + gio/tests/gdbus-auth.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 163 insertions(+) + +diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c +index 9e31c83..a5938c9 100644 +--- a/gio/gdbusauth.c ++++ b/gio/gdbusauth.c +@@ -260,6 +260,21 @@ find_mech_by_name (GDBusAuth *auth, + return ret; + } + ++static size_t ++get_longest_mechanism_name_length (GDBusAuth *auth) ++{ ++ size_t len = 0; ++ ++ for (GList *l = auth->priv->available_mechanisms; l != NULL; l = l->next) ++ { ++ Mechanism *m = l->data; ++ ++ len = MAX (len, strlen (m->name)); ++ } ++ ++ return len; ++} ++ + GDBusAuth * + _g_dbus_auth_new (GIOStream *stream) + { +@@ -268,6 +283,20 @@ _g_dbus_auth_new (GIOStream *stream) + NULL); + } + ++/* Arbitrarily chosen limit on the length of a DATA command payload, to prevent ++ * unbounded reads from malicious clients. ++ * ++ * - The ANONYMOUS mechanism doesn’t use DATA. ++ * - The EXTERNAL mechanism just uses it to transfer a decimal-encoded UID. ++ * - The DBUS_COOKIE_SHA1 mechanism transfers a challenge and a SHA1 hash. The ++ * hash is bounded in length, but the challenge is not, so could potentially ++ * hit this limit. It doesn’t seem unreasonable to bound the challenge to ++ * ~4KB though. GDBus itself generates a 16 byte challenge. ++ * ++ * See https://dbus.freedesktop.org/doc/dbus-specification.html#auth-command-data ++ */ ++#define MAX_DATA_PAYLOAD_LENGTH_BYTES 4096 ++ + /* ---------------------------------------------------------------------------------------------------- */ + /* like g_data_input_stream_read_line() but sets error if there's no content to read */ + static gchar * +@@ -305,6 +334,7 @@ _my_g_data_input_stream_read_line (GDataInputStream *dis, + */ + static gchar * + _my_g_input_stream_read_line_safe (GInputStream *i, ++ size_t max_line_length, + gsize *out_line_length, + GCancellable *cancellable, + GError **error) +@@ -314,11 +344,22 @@ _my_g_input_stream_read_line_safe (GInputStream *i, + gssize num_read; + gboolean last_was_cr; + ++ g_assert (max_line_length <= SIZE_MAX - 2); ++ + str = g_string_new (NULL); + + last_was_cr = FALSE; + while (TRUE) + { ++ if (str->len >= max_line_length + 2 /* allow for \r\n */) ++ { ++ g_set_error_literal (error, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ _("Malformed D-Bus authentication line")); ++ goto fail; ++ } ++ + num_read = g_input_stream_read (i, + &c, + 1, +@@ -1071,6 +1112,7 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + case SERVER_STATE_WAITING_FOR_AUTH: + debug_print ("SERVER: WaitingForAuth"); + line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream), ++ strlen ("AUTH ") + get_longest_mechanism_name_length (auth) + strlen (" ") + MAX_DATA_PAYLOAD_LENGTH_BYTES, + &line_length, + cancellable, + error); +@@ -1292,6 +1334,7 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + case SERVER_STATE_WAITING_FOR_DATA: + debug_print ("SERVER: WaitingForData"); + line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream), ++ strlen ("DATA ") + MAX_DATA_PAYLOAD_LENGTH_BYTES, + &line_length, + cancellable, + error); +@@ -1334,6 +1377,7 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + case SERVER_STATE_WAITING_FOR_BEGIN: + debug_print ("SERVER: WaitingForBegin"); + line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream), ++ MAX (strlen ("BEGIN"), strlen ("NEGOTIATE_UNIX_FD")), + &line_length, + cancellable, + error); +diff --git a/gio/tests/gdbus-auth.c b/gio/tests/gdbus-auth.c +index 657571b..3323d6e 100644 +--- a/gio/tests/gdbus-auth.c ++++ b/gio/tests/gdbus-auth.c +@@ -263,6 +263,124 @@ temp_dbus_keyrings_teardown (void) + g_unsetenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION"); + } + ++static void ++async_result_cb (GObject *obj, ++ GAsyncResult *result, ++ void *user_data) ++{ ++ GAsyncResult **result_out = user_data; ++ ++ g_assert (result_out != NULL); ++ g_assert (*result_out == NULL); ++ ++ *result_out = g_object_ref (result); ++ g_main_context_wakeup (g_main_context_get_thread_default ()); ++} ++ ++static gboolean ++server_new_connection_unexpected_cb (GDBusServer *server, ++ GDBusConnection *connection, ++ void *user_data) ++{ ++ g_assert_not_reached (); ++ return FALSE; ++} ++ ++static void ++test_auth_server_read_limit (void) ++{ ++ GDBusServer *server = NULL; ++ unsigned long new_connection_id = 0; ++ const char *server_address; ++ GIOStream *client_stream = NULL; ++ GOutputStream *client_output_stream; ++ GInputStream *client_input_stream; ++ GAsyncResult *result = NULL; ++ char *write_buffer = NULL; ++ char read_buffer[100]; ++ ssize_t read_len; ++ size_t bytes_written; ++ GError *local_error = NULL; ++ ++ g_test_summary ("Test that GDBusServer limits the lengths of reads it does during auth from a client"); ++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3985"); ++ ++ server = server_new_for_mechanism (NULL); ++ ++ new_connection_id = g_signal_connect (server, ++ "new-connection", ++ G_CALLBACK (server_new_connection_unexpected_cb), ++ NULL); ++ server_address = g_dbus_server_get_client_address (server); ++ g_dbus_server_start (server); ++ ++ /* Start connecting as a client */ ++ g_dbus_address_get_stream (server_address, NULL, async_result_cb, &result); ++ ++ while (result == NULL) ++ g_main_context_iteration (NULL, TRUE); ++ ++ client_stream = g_dbus_address_get_stream_finish (result, NULL, &local_error); ++ g_assert_no_error (local_error); ++ g_clear_object (&result); ++ ++ /* Send an over-long AUTH line, maliciously */ ++ client_output_stream = g_io_stream_get_output_stream (client_stream); ++ client_input_stream = g_io_stream_get_input_stream (client_stream); ++ ++ write_buffer = g_strdup_printf ("AUTH DBUS_COOKIE_SHA1 context%0*d 123 456\r\n", 5000, 0); ++ ++ g_output_stream_write_all_async (client_output_stream, ++ write_buffer, ++ strlen (write_buffer), ++ G_PRIORITY_DEFAULT, ++ NULL, ++ async_result_cb, ++ &result); ++ ++ while (result == NULL) ++ g_main_context_iteration (NULL, TRUE); ++ ++ g_output_stream_write_all_finish (client_output_stream, result, &bytes_written, &local_error); ++ g_assert_no_error (local_error); ++ g_assert_cmpuint (bytes_written, ==, strlen (write_buffer)); ++ g_clear_object (&result); ++ ++ g_clear_pointer (&write_buffer, g_free); ++ ++ /* Authentication should have been rejected, so reading or writing the stream ++ * should now fail. */ ++ read_len = g_input_stream_read (client_input_stream, ++ read_buffer, ++ sizeof (read_buffer), ++ NULL, ++ &local_error); ++ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED); ++ g_assert_cmpint (read_len, <, 0); ++ g_clear_error (&local_error); ++ ++ write_buffer = g_strdup_printf ("AUTH\r\n"); ++ ++ g_output_stream_write_all (client_output_stream, ++ write_buffer, ++ strlen (write_buffer), ++ &bytes_written, ++ NULL, ++ &local_error); ++ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED); ++ g_assert_cmpuint (bytes_written, ==, 0); ++ g_clear_error (&local_error); ++ ++ g_clear_pointer (&write_buffer, g_free); ++ ++ /* Cleanup */ ++ g_clear_object (&client_stream); ++ g_dbus_server_stop (server); ++ ++ g_clear_signal_handler (&new_connection_id, server); ++ g_clear_object (&server); ++} ++ + /* ---------------------------------------------------------------------------------------------------- */ + + int +@@ -282,6 +400,7 @@ main (int argc, + g_test_add_func ("/gdbus/auth/server/ANONYMOUS", auth_server_anonymous); + g_test_add_func ("/gdbus/auth/server/EXTERNAL", auth_server_external); + g_test_add_func ("/gdbus/auth/server/DBUS_COOKIE_SHA1", auth_server_dbus_cookie_sha1); ++ g_test_add_func ("/gdbus/auth/server/read-limit", test_auth_server_read_limit); + + /* TODO: we currently don't have tests for + * diff -Nru glib2.0-2.84.4/debian/patches/gdbusauthmechanism-Expose-client-reject-reason-as-a-new-v.patch glib2.0-2.84.4/debian/patches/gdbusauthmechanism-Expose-client-reject-reason-as-a-new-v.patch --- glib2.0-2.84.4/debian/patches/gdbusauthmechanism-Expose-client-reject-reason-as-a-new-v.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdbusauthmechanism-Expose-client-reject-reason-as-a-new-v.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,189 @@ +From: Philip Withnall +Date: Tue, 28 Apr 2026 15:51:00 +0100 +Subject: gdbusauthmechanism: Expose client reject reason as a new vfunc + +We can do this because `gdbusauthmechanism.h` is a private header. + +Hook it up to the existing `reject_reason` code in each +`GDBusAuthMechanism` implementation, as all three implementations +currently intermingle reject reasons from the server and client code, so +there would currently be no benefit to having a separate server and +client implementation of `*_get_reject_reason()`. + +This new private API will be used in a new unit test in the following +commit. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.88.1, commit:060aea67de7517d531b8fe2cdc07aa1a00ddeb22 +--- + gio/gdbusauthmechanism.c | 7 +++++++ + gio/gdbusauthmechanism.h | 2 ++ + gio/gdbusauthmechanismanon.c | 8 ++++---- + gio/gdbusauthmechanismexternal.c | 8 ++++---- + gio/gdbusauthmechanismsha1.c | 8 ++++---- + 5 files changed, 21 insertions(+), 12 deletions(-) + +diff --git a/gio/gdbusauthmechanism.c b/gio/gdbusauthmechanism.c +index be1b1dc..0b52f17 100644 +--- a/gio/gdbusauthmechanism.c ++++ b/gio/gdbusauthmechanism.c +@@ -324,6 +324,13 @@ _g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism, + return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_send (mechanism, out_data_len); + } + ++gchar * ++_g_dbus_auth_mechanism_client_get_reject_reason (GDBusAuthMechanism *mechanism) ++{ ++ g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL); ++ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_reject_reason (mechanism); ++} ++ + void + _g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism) + { +diff --git a/gio/gdbusauthmechanism.h b/gio/gdbusauthmechanism.h +index f0edd19..e906a47 100644 +--- a/gio/gdbusauthmechanism.h ++++ b/gio/gdbusauthmechanism.h +@@ -100,6 +100,7 @@ struct _GDBusAuthMechanismClass + gsize data_len); + gchar *(*client_data_send) (GDBusAuthMechanism *mechanism, + gsize *out_data_len); ++ gchar *(*client_get_reject_reason) (GDBusAuthMechanism *mechanism); + void (*client_shutdown) (GDBusAuthMechanism *mechanism); + }; + +@@ -148,6 +149,7 @@ void _g_dbus_auth_mechanism_client_data_receive (GDBus + gsize data_len); + gchar *_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); ++gchar *_g_dbus_auth_mechanism_client_get_reject_reason (GDBusAuthMechanism *mechanism); + void _g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism); + + +diff --git a/gio/gdbusauthmechanismanon.c b/gio/gdbusauthmechanismanon.c +index 5f59d4a..3d80ec1 100644 +--- a/gio/gdbusauthmechanismanon.c ++++ b/gio/gdbusauthmechanismanon.c +@@ -56,7 +56,7 @@ static void mechanism_server_data_receive (GDBusAuthMe + gsize data_len); + static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +-static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism); ++static gchar *mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism); + static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism); + static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism); + static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism, +@@ -103,12 +103,13 @@ _g_dbus_auth_mechanism_anon_class_init (GDBusAuthMechanismAnonClass *klass) + mechanism_class->server_initiate = mechanism_server_initiate; + mechanism_class->server_data_receive = mechanism_server_data_receive; + mechanism_class->server_data_send = mechanism_server_data_send; +- mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason; ++ mechanism_class->server_get_reject_reason = mechanism_server_or_client_get_reject_reason; + mechanism_class->server_shutdown = mechanism_server_shutdown; + mechanism_class->client_get_state = mechanism_client_get_state; + mechanism_class->client_initiate = mechanism_client_initiate; + mechanism_class->client_data_receive = mechanism_client_data_receive; + mechanism_class->client_data_send = mechanism_client_data_send; ++ mechanism_class->client_get_reject_reason = mechanism_server_or_client_get_reject_reason; + mechanism_class->client_shutdown = mechanism_client_shutdown; + } + +@@ -222,12 +223,11 @@ mechanism_server_data_send (GDBusAuthMechanism *mechanism, + } + + static gchar * +-mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) ++mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism) + { + GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL); +- g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL); + + /* can never end up here because we are never in the REJECTED state */ +diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c +index f7cb1b1..fce32fa 100644 +--- a/gio/gdbusauthmechanismexternal.c ++++ b/gio/gdbusauthmechanismexternal.c +@@ -64,7 +64,7 @@ static void mechanism_server_data_receive (GDBusAuthMe + gsize data_len); + static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +-static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism); ++static gchar *mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism); + static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism); + static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism); + static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism, +@@ -111,12 +111,13 @@ _g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *kla + mechanism_class->server_initiate = mechanism_server_initiate; + mechanism_class->server_data_receive = mechanism_server_data_receive; + mechanism_class->server_data_send = mechanism_server_data_send; +- mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason; ++ mechanism_class->server_get_reject_reason = mechanism_server_or_client_get_reject_reason; + mechanism_class->server_shutdown = mechanism_server_shutdown; + mechanism_class->client_get_state = mechanism_client_get_state; + mechanism_class->client_initiate = mechanism_client_initiate; + mechanism_class->client_data_receive = mechanism_client_data_receive; + mechanism_class->client_data_send = mechanism_client_data_send; ++ mechanism_class->client_get_reject_reason = mechanism_server_or_client_get_reject_reason; + mechanism_class->client_shutdown = mechanism_client_shutdown; + } + +@@ -321,12 +322,11 @@ mechanism_server_data_send (GDBusAuthMechanism *mechanism, + } + + static gchar * +-mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) ++mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism) + { + GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL); +- g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL); + + /* can never end up here because we are never in the REJECTED state */ +diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c +index ab59981..7c9e759 100644 +--- a/gio/gdbusauthmechanismsha1.c ++++ b/gio/gdbusauthmechanismsha1.c +@@ -120,7 +120,7 @@ static void mechanism_server_data_receive (GDBusAuthMe + gsize data_len); + static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism, + gsize *out_data_len); +-static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism); ++static gchar *mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism); + static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism); + static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism); + static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism, +@@ -173,12 +173,13 @@ _g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass) + mechanism_class->server_initiate = mechanism_server_initiate; + mechanism_class->server_data_receive = mechanism_server_data_receive; + mechanism_class->server_data_send = mechanism_server_data_send; +- mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason; ++ mechanism_class->server_get_reject_reason = mechanism_server_or_client_get_reject_reason; + mechanism_class->server_shutdown = mechanism_server_shutdown; + mechanism_class->client_get_state = mechanism_client_get_state; + mechanism_class->client_initiate = mechanism_client_initiate; + mechanism_class->client_data_receive = mechanism_client_data_receive; + mechanism_class->client_data_send = mechanism_client_data_send; ++ mechanism_class->client_get_reject_reason = mechanism_server_or_client_get_reject_reason; + mechanism_class->client_shutdown = mechanism_client_shutdown; + } + +@@ -1129,12 +1130,11 @@ mechanism_server_data_send (GDBusAuthMechanism *mechanism, + } + + static gchar * +-mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism) ++mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism) + { + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL); +- g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); + g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL); + + return g_strdup (m->priv->reject_reason); diff -Nru glib2.0-2.84.4/debian/patches/gdbusauthmechanismsha1-Improve-validation-of-cookie-ID.patch glib2.0-2.84.4/debian/patches/gdbusauthmechanismsha1-Improve-validation-of-cookie-ID.patch --- glib2.0-2.84.4/debian/patches/gdbusauthmechanismsha1-Improve-validation-of-cookie-ID.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdbusauthmechanismsha1-Improve-validation-of-cookie-ID.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,56 @@ +From: Philip Withnall +Date: Tue, 28 Apr 2026 15:49:54 +0100 +Subject: gdbusauthmechanismsha1: Improve validation of cookie ID +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +The D-Bus specification says the cookie ID has to be non-negative, but +we weren’t checking that (or checking that it was non-empty). + +Signed-off-by: Philip Withnall +[smcv: Backport to 2.84.x by adding ] +Origin: backport, 2.88.1, commit:c0531125344bb25fd66ffb7435ed6c285de09aeb +--- + gio/gdbusauthmechanismsha1.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c +index 7f348d8..ab59981 100644 +--- a/gio/gdbusauthmechanismsha1.c ++++ b/gio/gdbusauthmechanismsha1.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -1234,7 +1235,7 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism); + gchar **tokens; + const gchar *cookie_context; +- guint cookie_id; ++ int64_t cookie_id; + const gchar *server_challenge; + gchar *client_challenge; + gchar *endp; +@@ -1269,7 +1270,7 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + } + + cookie_id = g_ascii_strtoll (tokens[1], &endp, 10); +- if (*endp != '\0') ++ if (*endp != '\0' || endp == tokens[1] || cookie_id < 0 || cookie_id > UINT32_MAX) + { + g_free (m->priv->reject_reason); + m->priv->reject_reason = g_strdup_printf ("Malformed cookie_id '%s'", tokens[1]); +@@ -1279,7 +1280,7 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + server_challenge = tokens[2]; + + error = NULL; +- cookie = keyring_lookup_entry (cookie_context, cookie_id, &error); ++ cookie = keyring_lookup_entry (cookie_context, (unsigned int) cookie_id, &error); + if (cookie == NULL) + { + g_free (m->priv->reject_reason); diff -Nru glib2.0-2.84.4/debian/patches/gdbusauthmechanismsha1-Validate-cookie-context.patch glib2.0-2.84.4/debian/patches/gdbusauthmechanismsha1-Validate-cookie-context.patch --- glib2.0-2.84.4/debian/patches/gdbusauthmechanismsha1-Validate-cookie-context.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdbusauthmechanismsha1-Validate-cookie-context.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,77 @@ +From: Philip Withnall +Date: Tue, 28 Apr 2026 15:47:30 +0100 +Subject: gdbusauthmechanismsha1: Validate cookie context +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +Without validation, the server could send a malicious context which +contains path traversal characters, allowing it to exfiltrate a SHA-1 +hashed copy of arbitrary data from the client’s file system. + +To exploit this successfully would require the client to choose to +connect peer-to-peer to a malicious D-Bus server and to choose the SHA-1 +authentication mechanism in preference to all the other mechanisms. This +is vanishingly unlikely. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.88.1, commit:db9c8fae398b0c457e660ce63dd5afec8993046a +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3931 +--- + gio/gdbusauthmechanismsha1.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c +index c8aa089..7f348d8 100644 +--- a/gio/gdbusauthmechanismsha1.c ++++ b/gio/gdbusauthmechanismsha1.c +@@ -1198,6 +1198,34 @@ mechanism_client_initiate (GDBusAuthMechanism *mechanism, + return initial_response; + } + ++/* Context names must be valid ASCII, nonzero length, and may not contain the ++ * characters slash ("/"), backslash ("\"), space (" "), newline ("\n"), ++ * carriage return ("\r"), tab ("\t"), or period ("."). ++ * ++ * See https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-sha */ ++static gboolean ++validate_cookie_context (const char *cookie_context) ++{ ++ size_t i = 0; ++ ++ g_return_val_if_fail (cookie_context != NULL, FALSE); ++ ++ for (i = 0; cookie_context[i] != '\0'; i++) ++ { ++ if ((uint8_t) cookie_context[i] >= 128 || ++ cookie_context[i] == '/' || ++ cookie_context[i] == '\\' || ++ cookie_context[i] == ' ' || ++ cookie_context[i] == '\n' || ++ cookie_context[i] == '\r' || ++ cookie_context[i] == '\t' || ++ cookie_context[i] == '.') ++ return FALSE; ++ } ++ ++ return (i > 0); ++} ++ + static void + mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + const gchar *data, +@@ -1232,6 +1260,14 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism, + } + + cookie_context = tokens[0]; ++ if (!validate_cookie_context (tokens[0])) ++ { ++ g_free (m->priv->reject_reason); ++ m->priv->reject_reason = g_strdup_printf ("Malformed cookie_context '%s'", tokens[0]); ++ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; ++ goto out; ++ } ++ + cookie_id = g_ascii_strtoll (tokens[1], &endp, 10); + if (*endp != '\0') + { diff -Nru glib2.0-2.84.4/debian/patches/gdbusintrospection-Add-some-assertions-before-array-deref.patch glib2.0-2.84.4/debian/patches/gdbusintrospection-Add-some-assertions-before-array-deref.patch --- glib2.0-2.84.4/debian/patches/gdbusintrospection-Add-some-assertions-before-array-deref.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdbusintrospection-Add-some-assertions-before-array-deref.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,90 @@ +From: Philip Withnall +Date: Thu, 16 Apr 2026 15:08:10 +0100 +Subject: gdbusintrospection: Add some assertions before array dereferences +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +The state handling inside the D-Bus introspection XML parser is +complicated, and it’s possible that these dereferences of the +`len - 1`th element might get reached when the array is empty. + +Make failures like that more debuggable by adding an assertion on the +length beforehand. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.89.0, commit:656ad4582cb1d7a7fa8bafe3ce8aec6aa3c17da0 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3932 +Bug-Debian: https://bugs.debian.org/1141316 +--- + gio/gdbusintrospection.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c +index df5daa3..1138ba9 100644 +--- a/gio/gdbusintrospection.c ++++ b/gio/gdbusintrospection.c +@@ -1096,6 +1096,7 @@ parse_data_get_annotation (ParseData *data, + { + if (create_new) + g_ptr_array_add (data->annotations, g_new0 (GDBusAnnotationInfo, 1)); ++ g_assert (data->annotations->len > 0); + return data->annotations->pdata[data->annotations->len - 1]; + } + +@@ -1105,6 +1106,7 @@ parse_data_get_arg (ParseData *data, + { + if (create_new) + g_ptr_array_add (data->args, g_new0 (GDBusArgInfo, 1)); ++ g_assert (data->args->len > 0); + return data->args->pdata[data->args->len - 1]; + } + +@@ -1114,6 +1116,7 @@ parse_data_get_out_arg (ParseData *data, + { + if (create_new) + g_ptr_array_add (data->out_args, g_new0 (GDBusArgInfo, 1)); ++ g_assert (data->out_args->len > 0); + return data->out_args->pdata[data->out_args->len - 1]; + } + +@@ -1123,6 +1126,7 @@ parse_data_get_method (ParseData *data, + { + if (create_new) + g_ptr_array_add (data->methods, g_new0 (GDBusMethodInfo, 1)); ++ g_assert (data->methods->len > 0); + return data->methods->pdata[data->methods->len - 1]; + } + +@@ -1132,6 +1136,7 @@ parse_data_get_signal (ParseData *data, + { + if (create_new) + g_ptr_array_add (data->signals, g_new0 (GDBusSignalInfo, 1)); ++ g_assert (data->signals->len > 0); + return data->signals->pdata[data->signals->len - 1]; + } + +@@ -1141,6 +1146,7 @@ parse_data_get_property (ParseData *data, + { + if (create_new) + g_ptr_array_add (data->properties, g_new0 (GDBusPropertyInfo, 1)); ++ g_assert (data->properties->len > 0); + return data->properties->pdata[data->properties->len - 1]; + } + +@@ -1150,6 +1156,7 @@ parse_data_get_interface (ParseData *data, + { + if (create_new) + g_ptr_array_add (data->interfaces, g_new0 (GDBusInterfaceInfo, 1)); ++ g_assert (data->interfaces->len > 0); + return data->interfaces->pdata[data->interfaces->len - 1]; + } + +@@ -1159,6 +1166,7 @@ parse_data_get_node (ParseData *data, + { + if (create_new) + g_ptr_array_add (data->nodes, g_new0 (GDBusNodeInfo, 1)); ++ g_assert (data->nodes->len > 0); + return data->nodes->pdata[data->nodes->len - 1]; + } + diff -Nru glib2.0-2.84.4/debian/patches/gdbusintrospection-Fix-XML-parser-state-handling-for-node.patch glib2.0-2.84.4/debian/patches/gdbusintrospection-Fix-XML-parser-state-handling-for-node.patch --- glib2.0-2.84.4/debian/patches/gdbusintrospection-Fix-XML-parser-state-handling-for-node.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdbusintrospection-Fix-XML-parser-state-handling-for-node.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,89 @@ +From: Philip Withnall +Date: Thu, 16 Apr 2026 15:27:37 +0100 +Subject: gdbusintrospection: Fix XML parser state handling for element + nesting + +The check for whether a `` element in D-Bus introspection XML was +nested correctly was broken. `` elements can only be at the top +level, or nested immediately within another `` element. + +Fix the check and add some unit tests for it. + +Spotted by linhlhq as #YWH-PGM9867-204. The fix is mine, and the unit test +uses example XML strings adapted from their report. + +Signed-off-by: Philip Withnall + +Origin: upstream, 2.89.0, commit:c9da977c178fbfc0e4caf99f9fdf5dc433d6fcc2 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3932 +Bug-CVE: CVE-2026-58016 +Bug-Debian: https://bugs.debian.org/1141316 +--- + gio/gdbusintrospection.c | 2 +- + gio/tests/gdbus-introspection.c | 33 +++++++++++++++++++++++++++++++++ + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c +index 1138ba9..4df8e87 100644 +--- a/gio/gdbusintrospection.c ++++ b/gio/gdbusintrospection.c +@@ -1266,7 +1266,7 @@ parser_start_element (GMarkupParseContext *context, + /* ---------------------------------------------------------------------------------------------------- */ + if (strcmp (element_name, "node") == 0) + { +- if (!(g_slist_length (stack) >= 1 || strcmp (stack->next->data, "node") != 0)) ++ if (stack->next != NULL && strcmp (stack->next->data, "node") != 0) + { + g_set_error_literal (error, + G_MARKUP_ERROR, +diff --git a/gio/tests/gdbus-introspection.c b/gio/tests/gdbus-introspection.c +index f088475..b755aae 100644 +--- a/gio/tests/gdbus-introspection.c ++++ b/gio/tests/gdbus-introspection.c +@@ -300,6 +300,38 @@ test_extra_data (void) + g_dbus_node_info_unref (info); + } + ++static void ++test_invalid (void) ++{ ++ const struct ++ { ++ const char *xml; ++ GMarkupError expected_error_code; ++ } ++ vectors[] = ++ { ++ { "", G_MARKUP_ERROR_EMPTY }, ++ { "", G_MARKUP_ERROR_INVALID_CONTENT }, ++ { "", G_MARKUP_ERROR_INVALID_CONTENT }, ++ { "", G_MARKUP_ERROR_INVALID_CONTENT }, ++ { "", G_MARKUP_ERROR_INVALID_CONTENT }, ++ }; ++ ++ for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++) ++ { ++ GDBusNodeInfo *node; ++ GError *local_error = NULL; ++ ++ g_test_message ("Testing parsing of %s gives an error", vectors[i].xml); ++ ++ node = g_dbus_node_info_new_for_xml (vectors[i].xml, &local_error); ++ g_assert_error (local_error, G_MARKUP_ERROR, (int) vectors[i].expected_error_code); ++ g_assert_null (node); ++ ++ g_clear_error (&local_error); ++ } ++} ++ + /* ---------------------------------------------------------------------------------------------------- */ + + int +@@ -317,6 +349,7 @@ main (int argc, + g_test_add_func ("/gdbus/introspection/generate", test_generate); + g_test_add_func ("/gdbus/introspection/default-direction", test_default_direction); + g_test_add_func ("/gdbus/introspection/extra-data", test_extra_data); ++ g_test_add_func ("/gdbus/introspection/invalid", test_invalid); + + ret = session_bus_run (); + diff -Nru glib2.0-2.84.4/debian/patches/gdbusmessage-Fix-types-of-integer-arithmetic-in-message-l.patch glib2.0-2.84.4/debian/patches/gdbusmessage-Fix-types-of-integer-arithmetic-in-message-l.patch --- glib2.0-2.84.4/debian/patches/gdbusmessage-Fix-types-of-integer-arithmetic-in-message-l.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gdbusmessage-Fix-types-of-integer-arithmetic-in-message-l.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,141 @@ +From: Philip Withnall +Date: Mon, 20 Apr 2026 14:51:19 +0100 +Subject: gdbusmessage: Fix types of integer arithmetic in message length + calculation + +This fixes the addition happening with type `uint32_t`, rather than the +expected `gssize`. Because of the narrower type, this could have +resulted in integer overflow for large header lengths, bypassing the +message length check lower down in the function. + +This is not exploitable in practice unless communicating peer-to-peer +with a malicious D-Bus peer, as invalid messages like this would be +filtered out by the message bus (such as `dbus-daemon`). + +Rework the arithmetic to use checked arithmetic functions into a +`size_t` (largely as suggested by Simon McVittie), and add some unit +tests. Simply doing the arithmetic in the `gssize` domain would not fix +the issue on 32-bit systems. + +Based on a report by Koda Reef, albeit reverse engineered as the report +did not contain a proof of concept of the vulnerability. + +Signed-off-by: Philip Withnall +[smcv: Backport to 2.84.x by adding ] +Origin: backport, 2.88.1, commit:e28262f16979f3a2f5293233e5dae102cb47d61c +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3933 +--- + gio/gdbusmessage.c | 39 +++++++++++++++++++++------------------ + gio/tests/gdbus-message.c | 16 ++++++++++++++++ + 2 files changed, 37 insertions(+), 18 deletions(-) + +diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c +index ad7dd2e..ff6e53c 100644 +--- a/gio/gdbusmessage.c ++++ b/gio/gdbusmessage.c +@@ -27,6 +27,7 @@ + + #include + #include ++#include + #include + #include + +@@ -2273,9 +2274,8 @@ g_dbus_message_bytes_needed (guchar *blob, + gsize blob_len, + GError **error) + { +- gssize ret; +- +- ret = -1; ++ uint32_t header_len, body_len; ++ size_t ret; + + g_return_val_if_fail (blob != NULL, -1); + g_return_val_if_fail (error == NULL || *error == NULL, -1); +@@ -2283,21 +2283,13 @@ g_dbus_message_bytes_needed (guchar *blob, + + if (blob[0] == 'l') + { +- /* core header (12 bytes) + ARRAY of STRUCT of (BYTE,VARIANT) */ +- ret = 12 + 4 + GUINT32_FROM_LE (((guint32 *) blob)[3]); +- /* round up so it's a multiple of 8 */ +- ret = 8 * ((ret + 7)/8); +- /* finally add the body size */ +- ret += GUINT32_FROM_LE (((guint32 *) blob)[1]); ++ header_len = GUINT32_FROM_LE (((guint32 *) blob)[3]); ++ body_len = GUINT32_FROM_LE (((guint32 *) blob)[1]); + } + else if (blob[0] == 'B') + { +- /* core header (12 bytes) + ARRAY of STRUCT of (BYTE,VARIANT) */ +- ret = 12 + 4 + GUINT32_FROM_BE (((guint32 *) blob)[3]); +- /* round up so it's a multiple of 8 */ +- ret = 8 * ((ret + 7)/8); +- /* finally add the body size */ +- ret += GUINT32_FROM_BE (((guint32 *) blob)[1]); ++ header_len = GUINT32_FROM_BE (((guint32 *) blob)[3]); ++ body_len = GUINT32_FROM_BE (((guint32 *) blob)[1]); + } + else + { +@@ -2305,18 +2297,29 @@ g_dbus_message_bytes_needed (guchar *blob, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Unable to determine message blob length - given blob is malformed"); ++ return -1; + } + +- if (ret > (1<<27)) ++ /* core header (12 bytes) + array length (4 bytes) + length of array. ++ * Array elements are tuples: (byte, variant) */ ++ ret = 0; ++ if (!g_size_checked_add (&ret, 12 + 4, header_len) || ++ /* round up so it's a multiple of 8: ret = 8 * ((ret + 7)/8) */ ++ !g_size_checked_add (&ret, ret, 7) || ++ !g_size_checked_mul (&ret, 8, ret/8) || ++ /* finally add the body size */ ++ !g_size_checked_add (&ret, ret, body_len) || ++ ret > (1 << 27)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Blob indicates that message exceeds maximum message length (128MiB)"); +- ret = -1; ++ return -1; + } + +- return ret; ++ g_assert (ret <= G_MAXSSIZE); ++ return (gssize) ret; + } + + /* ---------------------------------------------------------------------------------------------------- */ +diff --git a/gio/tests/gdbus-message.c b/gio/tests/gdbus-message.c +index 647cd7a..84487b7 100644 +--- a/gio/tests/gdbus-message.c ++++ b/gio/tests/gdbus-message.c +@@ -185,6 +185,22 @@ message_bytes_needed (void) + 0, 0, 0, 0x08, /* body length (128MiB) */ + 1, 0, 0, 0, /* message serial */ + 7, 0, 0, 0 /* header length */}, -1 }, ++ { { 'B', 0, 0, 1, /* endianness, message type, flags, protocol version */ ++ 0, 0, 0, 0, /* body length (empty) */ ++ 1, 0, 0, 0, /* message serial */ ++ 0xff, 0xff, 0xff, 0xf0 /* header length (overflow) */}, -1 }, ++ { { 'l', 0, 0, 1, /* endianness, message type, flags, protocol version */ ++ 0, 0, 0, 0, /* body length (empty) */ ++ 1, 0, 0, 0, /* message serial */ ++ 0xf0, 0xff, 0xff, 0xff /* header length (overflow) */}, -1 }, ++ { { 'B', 0, 0, 1, /* endianness, message type, flags, protocol version */ ++ 0, 0, 0, 1, /* body length (short) */ ++ 1, 0, 0, 0, /* message serial */ ++ 0xff, 0xff, 0xff, 0xef /* header length (overflow once body is added) */}, -1 }, ++ { { 'l', 0, 0, 1, /* endianness, message type, flags, protocol version */ ++ 1, 0, 0, 0, /* body length (short) */ ++ 1, 0, 0, 0, /* message serial */ ++ 0xef, 0xff, 0xff, 0xff /* header length (overflow once body is added) */}, -1 }, + }; + gsize i; + diff -Nru glib2.0-2.84.4/debian/patches/giochannel-Fix-memcmp-off-the-end-of-the-buffer-with-long.patch glib2.0-2.84.4/debian/patches/giochannel-Fix-memcmp-off-the-end-of-the-buffer-with-long.patch --- glib2.0-2.84.4/debian/patches/giochannel-Fix-memcmp-off-the-end-of-the-buffer-with-long.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/giochannel-Fix-memcmp-off-the-end-of-the-buffer-with-long.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,131 @@ +From: Philip Withnall +Date: Tue, 28 Apr 2026 16:45:14 +0100 +Subject: giochannel: Fix memcmp() off the end of the buffer with long + terminators +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +If the line terminator is longer than a single byte, and the current +line extends to the end of the buffer, and the buffer (which is a +`GString`) is near a power of two in length (as that’s how `GString`s +are allocated) it’s possible for the `memcmp()` which checks the +terminator to read off the end of the string buffer. + +Fix that by checking the terminator length against the last character +before calling `memcmp()`. Add a unit test. + +Spotted by linhlhq as #YWH-PGM9867-199. The fix is theirs (validated by +me), and the unit test is adapted from their proof of concept. + +Signed-off-by: Philip Withnall +[smcv: Backport to 2.84.x by adding ] +Origin: backport, 2.88.1, commit:6a2583dec39bfe05553b16d9b7419d6c2a257244 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3925 +Bug-CVE: CVE-2026-58013 +--- + glib/giochannel.c | 3 ++- + glib/tests/io-channel.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 64 insertions(+), 1 deletion(-) + +diff --git a/glib/giochannel.c b/glib/giochannel.c +index e54aea2..640f698 100644 +--- a/glib/giochannel.c ++++ b/glib/giochannel.c +@@ -1828,7 +1828,8 @@ read_again: + { + if (channel->line_term) + { +- if (memcmp (channel->line_term, nextchar, line_term_len) == 0) ++ if ((size_t) (lastchar - nextchar) >= line_term_len && ++ memcmp (channel->line_term, nextchar, line_term_len) == 0) + { + line_length = nextchar - use_buf->str; + got_term_len = line_term_len; +diff --git a/glib/tests/io-channel.c b/glib/tests/io-channel.c +index cab4869..d7e7876 100644 +--- a/glib/tests/io-channel.c ++++ b/glib/tests/io-channel.c +@@ -28,6 +28,8 @@ + * Author: Philip Withnall + */ + ++#include ++ + #include + #include + +@@ -228,6 +230,65 @@ test_read_line_embedded_nuls (void) + g_free (filename); + } + ++static void ++test_read_line_long_terminator (void) ++{ ++ uint8_t *test_data = NULL; ++ size_t test_data_len = 0; ++ int fd; ++ char *filename = NULL; ++ GIOChannel *channel = NULL; ++ GError *local_error = NULL; ++ char *line = NULL; ++ size_t line_length, terminator_pos; ++ const char *line_term; ++ int line_term_length; ++ GIOStatus status; ++ ++ g_test_summary ("Test that reading a line when using a long terminator doesn’t over-read the buffer."); ++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/work_items/3925"); ++ ++ /* Write out a temporary file containing 2047 bytes. This is enough to make it ++ * near the length of the GString buffer when read back in. */ ++ fd = g_file_open_tmp ("glib-test-io-channel-XXXXXX", &filename, &local_error); ++ g_assert_no_error (local_error); ++ g_close (g_steal_fd (&fd), NULL); ++ ++ test_data_len = 2047; ++ test_data = g_malloc (test_data_len); ++ memset (test_data, 'M', test_data_len); ++ g_file_set_contents (filename, (const gchar *) test_data, test_data_len, &local_error); ++ g_assert_no_error (local_error); ++ ++ /* Create the channel. */ ++ channel = g_io_channel_new_file (filename, "r", &local_error); ++ g_assert_no_error (local_error); ++ ++ /* Use a long line terminator so it could potentially over-read the end of the buffer. */ ++ g_io_channel_set_line_term (channel, "DEADBEEF", 8); ++ ++ line_term = g_io_channel_get_line_term (channel, &line_term_length); ++ g_assert_cmpstr (line_term, ==, "DEADBEEF"); ++ g_assert_cmpint (line_term_length, ==, 8); ++ ++ g_io_channel_set_encoding (channel, "UTF-8", &local_error); ++ g_assert_no_error (local_error); ++ ++ status = g_io_channel_read_line (channel, &line, &line_length, ++ &terminator_pos, &local_error); ++ g_assert_no_error (local_error); ++ g_assert_cmpint (status, ==, G_IO_STATUS_NORMAL); ++ g_assert_cmpuint (line_length, ==, 2047); ++ g_assert_cmpuint (terminator_pos, ==, 2047); ++ g_assert_cmpmem (line, line_length, test_data, test_data_len); ++ ++ g_free (line); ++ g_io_channel_unref (channel); ++ g_free (test_data); ++ g_unlink (filename); ++ g_free (filename); ++} ++ + int + main (int argc, + char *argv[]) +@@ -236,6 +297,7 @@ main (int argc, + + g_test_add_func ("/io-channel/read-write", test_read_write); + g_test_add_func ("/io-channel/read-line/embedded-nuls", test_read_line_embedded_nuls); ++ g_test_add_func ("/io-channel/read-line/long-terminator", test_read_line_long_terminator); + + return g_test_run (); + } diff -Nru glib2.0-2.84.4/debian/patches/gkeyfile-Fix-a-one-byte-heap-under-read-with-g_key_file_g.patch glib2.0-2.84.4/debian/patches/gkeyfile-Fix-a-one-byte-heap-under-read-with-g_key_file_g.patch --- glib2.0-2.84.4/debian/patches/gkeyfile-Fix-a-one-byte-heap-under-read-with-g_key_file_g.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gkeyfile-Fix-a-one-byte-heap-under-read-with-g_key_file_g.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,102 @@ +From: Philip Withnall +Date: Sat, 11 Apr 2026 14:42:57 +0100 +Subject: gkeyfile: Fix a one-byte heap under-read with + g_key_file_get_locale_string_list() + +If this method was called on a key file key which has an empty value, +`len == 0` and this leads to a one-byte under-read off the start of the +key file buffer. + +Spotted by linhlhq as #YWH-PGM9867-200. The suggested fix is theirs, and +the unit test is adapted from their report. I added the fuzzing test. + +Signed-off-by: Philip Withnall + +Origin: upstream, 2.88.1, commit:94ecb5b44a1cae09f481dd5e693832f129948893 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3930 +Bug-CVE: CVE-2026-58014 +--- + fuzzing/fuzz_key.c | 9 +++++++++ + glib/gkeyfile.c | 2 +- + glib/tests/keyfile.c | 23 +++++++++++++++++++++++ + 3 files changed, 33 insertions(+), 1 deletion(-) + +diff --git a/fuzzing/fuzz_key.c b/fuzzing/fuzz_key.c +index 77cb684..7d00443 100644 +--- a/fuzzing/fuzz_key.c ++++ b/fuzzing/fuzz_key.c +@@ -26,11 +26,20 @@ test_parse (const gchar *data, + GKeyFileFlags flags) + { + GKeyFile *key = NULL; ++ char *comment = NULL; ++ char **list = NULL; + + key = g_key_file_new (); + g_key_file_load_from_data (key, (const gchar*) data, size, G_KEY_FILE_NONE, + NULL); + ++ /* Also try some additional parsing and see if it crashes */ ++ comment = g_key_file_get_comment (key, "group", "key", NULL); ++ g_free (comment); ++ ++ list = g_key_file_get_locale_string_list (key, "group", "key", "de", NULL, NULL); ++ g_strfreev (list); ++ + g_key_file_free (key); + } + +diff --git a/glib/gkeyfile.c b/glib/gkeyfile.c +index 92b3faf..b3cf43c 100644 +--- a/glib/gkeyfile.c ++++ b/glib/gkeyfile.c +@@ -2564,7 +2564,7 @@ g_key_file_get_locale_string_list (GKeyFile *key_file, + } + + len = strlen (value); +- if (value[len - 1] == key_file->list_separator) ++ if (len > 0 && value[len - 1] == key_file->list_separator) + value[len - 1] = '\0'; + + list_separator[0] = key_file->list_separator; +diff --git a/glib/tests/keyfile.c b/glib/tests/keyfile.c +index 64a9ec3..da5cad8 100644 +--- a/glib/tests/keyfile.c ++++ b/glib/tests/keyfile.c +@@ -889,6 +889,28 @@ test_locale_string_multiple_loads (void) + g_free (old_locale); + } + ++static void ++test_locale_string_empty (void) ++{ ++ GKeyFile *keyfile = NULL; ++ GError *local_error = NULL; ++ const char *data = ++ "[valid]\n" ++ "key1=\n"; ++ ++ g_test_summary ("Check that loading an empty translatable string works"); ++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3930"); ++ ++ keyfile = g_key_file_new (); ++ ++ g_key_file_load_from_data (keyfile, data, -1, G_KEY_FILE_NONE, &local_error); ++ g_assert_no_error (local_error); ++ ++ check_locale_string_list_value (keyfile, "valid", "key1", NULL, NULL); ++ ++ g_key_file_free (keyfile); ++} ++ + static void + test_lists (void) + { +@@ -2011,6 +2033,7 @@ main (int argc, char *argv[]) + g_test_add_func ("/keyfile/number", test_number); + g_test_add_func ("/keyfile/locale-string", test_locale_string); + g_test_add_func ("/keyfile/locale-string/multiple-loads", test_locale_string_multiple_loads); ++ g_test_add_func ("/keyfile/locale-string/empty", test_locale_string_empty); + g_test_add_func ("/keyfile/lists", test_lists); + g_test_add_func ("/keyfile/lists-set-get", test_lists_set_get); + g_test_add_func ("/keyfile/group-remove", test_group_remove); diff -Nru glib2.0-2.84.4/debian/patches/gmarkup-Fix-potential-one-byte-overread-in-g_markup_escap.patch glib2.0-2.84.4/debian/patches/gmarkup-Fix-potential-one-byte-overread-in-g_markup_escap.patch --- glib2.0-2.84.4/debian/patches/gmarkup-Fix-potential-one-byte-overread-in-g_markup_escap.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gmarkup-Fix-potential-one-byte-overread-in-g_markup_escap.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,96 @@ +From: Philip Withnall +Date: Sun, 29 Mar 2026 19:46:33 +0100 +Subject: gmarkup: Fix potential one-byte overread in g_markup_escape_text() +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +If the input string (which might be malicious, as the program is +choosing to escape it) ends with 0xc2 (a UTF-8 prefix byte), the code +wasn’t previously checking the input string length was long enough to +contain the following byte before reading it. + +Fix that by switching from `g_utf8_get_char()` to +`g_utf8_get_char_validated()`. + +Spotted by linhlhq as #YWH-PGM9867-187. Thank you for the suggested fix +and unit test, which have been adapted into this commit. + +Also spotted by oss-fuzz after we added a fuzz test for this function. + +Signed-off-by: Philip Withnall + +oss-fuzz#498475244 + +Origin: upstream, 2.86.5, commit:a316ec5005b1fd3b4ed7ae55603a844077862a06 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3916 +--- + glib/gmarkup.c | 8 ++++++-- + glib/tests/markup-escape.c | 16 ++++++++++++++-- + 2 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/glib/gmarkup.c b/glib/gmarkup.c +index f517974..4f79600 100644 +--- a/glib/gmarkup.c ++++ b/glib/gmarkup.c +@@ -2200,7 +2200,7 @@ append_escaped_text (GString *str, + /* The utf-8 control characters to escape begins with 0xc2 byte */ + else if (c == 0xc2) + { +- gunichar u = g_utf8_get_char (pending); ++ gunichar u = g_utf8_get_char_validated (pending, end - pending); + + if ((0x7f < u && u <= 0x84) || + (0x86 <= u && u <= 0x9f)) +@@ -2216,7 +2216,11 @@ append_escaped_text (GString *str, + p++; + } + else +- pending++; ++ { ++ /* Not the UTF-8 control characters we’re looking for, or an ++ * invalid or partial encoding. Pass it through. */ ++ pending++; ++ } + } + else + pending++; +diff --git a/glib/tests/markup-escape.c b/glib/tests/markup-escape.c +index 44290fe..d7a3a8b 100644 +--- a/glib/tests/markup-escape.c ++++ b/glib/tests/markup-escape.c +@@ -37,6 +37,7 @@ static EscapeTest escape_tests[] = + { "N\xc2\x80N", "N€N" }, + { "N\xc2\x79N", "N\xc2\x79N" }, + { "N\xc2\x9fN", "NŸN" }, ++ { "\xc2", "\xc2" }, + + /* As per g_markup_escape_text()'s documentation, whitespace is not escaped: */ + { "\t", "\t" }, +@@ -46,13 +47,24 @@ static void + escape_test (gconstpointer d) + { + const EscapeTest *test = d; +- gchar *result; ++ char *non_nul_terminated_original = NULL; ++ size_t non_nul_terminated_original_len = 0; ++ char *result = NULL, *result2 = NULL; + ++ /* Try once nul-terminated */ + result = g_markup_escape_text (test->original, -1); +- + g_assert_cmpstr (result, ==, test->expected); + ++ /* And try again with a newly allocated original without a nul-terminator, ++ * and using a fixed length. This can help catch buffer overflows. */ ++ non_nul_terminated_original_len = strlen (test->original); ++ non_nul_terminated_original = (non_nul_terminated_original_len > 0) ? g_memdup2 (test->original, non_nul_terminated_original_len) : g_strdup (test->original); ++ result2 = g_markup_escape_text (non_nul_terminated_original, non_nul_terminated_original_len); ++ g_assert_cmpstr (result2, ==, test->expected); ++ + g_free (result); ++ g_free (result2); ++ g_free (non_nul_terminated_original); + } + + typedef struct _UnicharTest UnicharTest; diff -Nru glib2.0-2.84.4/debian/patches/gregex-Fix-case-changing-substitutions-with-G_REGEX_RAW.patch glib2.0-2.84.4/debian/patches/gregex-Fix-case-changing-substitutions-with-G_REGEX_RAW.patch --- glib2.0-2.84.4/debian/patches/gregex-Fix-case-changing-substitutions-with-G_REGEX_RAW.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gregex-Fix-case-changing-substitutions-with-G_REGEX_RAW.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,221 @@ +From: Philip Withnall +Date: Tue, 31 Mar 2026 16:13:57 +0100 +Subject: gregex: Fix case changing substitutions with G_REGEX_RAW + +In `G_REGEX_RAW` mode, the input string is treated as a byte array +(basically ASCII) rather than a unichar array. Accordingly, the case +changing code for substitutions needs to operate on bytes with +`G_REGEX_RAW`, rather than operating on unichars. + +This fixes a potential buffer overflow when trying to do a case change +on a match of a set of bytes which are a truncated multi-byte UTF-8 +encoding at the end of the input buffer. + +Spotted by linhlhq as #YWH-PGM9867-193. I adapted their reproducer as +the unit test, but implemented the fix in `gregex.c` independently. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.86.5, commit:d337aabd24ee2b8ac2a690dba3ccf26aa70e638f +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3918 +Bug-CVE: CVE-2026-58012 +--- + glib/gregex.c | 59 ++++++++++++++++++++++++++++++++++++++++-------------- + glib/tests/regex.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 97 insertions(+), 15 deletions(-) + +diff --git a/glib/gregex.c b/glib/gregex.c +index 1e18384..ce269ef 100644 +--- a/glib/gregex.c ++++ b/glib/gregex.c +@@ -3146,19 +3146,25 @@ split_replacement (const gchar *replacement, + return g_list_reverse (list); + } + +-/* Change the case of c based on change_case. */ +-#define CHANGE_CASE(c, change_case) \ ++/* Change the case of c based on change_case. ++ * g_ascii_to*() will happily pass through non-ASCII bytes unchanged. */ ++#define UTF8_CHANGE_CASE(c, change_case) \ + (((change_case) & CHANGE_CASE_LOWER_MASK) ? \ + g_unichar_tolower (c) : \ + g_unichar_toupper (c)) ++#define RAW_CHANGE_CASE(c, change_case) \ ++ (((change_case) & CHANGE_CASE_LOWER_MASK) ? \ ++ g_ascii_tolower (c) : \ ++ g_ascii_toupper (c)) + ++/* If @text_is_raw is set, @text might not be valid UTF-8 (but will be ++ * nul-terminated). */ + static void + string_append (GString *string, + const gchar *text, ++ gboolean text_is_raw, + ChangeCase *change_case) + { +- gunichar c; +- + if (text[0] == '\0') + return; + +@@ -3168,22 +3174,44 @@ string_append (GString *string, + } + else if (*change_case & CHANGE_CASE_SINGLE_MASK) + { +- c = g_utf8_get_char (text); +- g_string_append_unichar (string, CHANGE_CASE (c, *change_case)); +- g_string_append (string, g_utf8_next_char (text)); ++ if (!text_is_raw) ++ { ++ gunichar c = g_utf8_get_char (text); ++ g_string_append_unichar (string, UTF8_CHANGE_CASE (c, *change_case)); ++ g_string_append (string, g_utf8_next_char (text)); ++ } ++ else ++ { ++ g_string_append_c (string, RAW_CHANGE_CASE (text[0], *change_case)); ++ g_string_append (string, text + 1); ++ } ++ + *change_case = CHANGE_CASE_NONE; + } + else + { +- while (*text != '\0') ++ if (!text_is_raw) + { +- c = g_utf8_get_char (text); +- g_string_append_unichar (string, CHANGE_CASE (c, *change_case)); +- text = g_utf8_next_char (text); ++ while (*text != '\0') ++ { ++ gunichar c = g_utf8_get_char (text); ++ g_string_append_unichar (string, UTF8_CHANGE_CASE (c, *change_case)); ++ text = g_utf8_next_char (text); ++ } ++ } ++ else ++ { ++ while (*text != '\0') ++ { ++ char c = *text; ++ g_string_append_c (string, RAW_CHANGE_CASE (c, *change_case)); ++ text++; ++ } + } + } + } + ++/* @match_info is (nullable) */ + static gboolean + interpolate_replacement (const GMatchInfo *match_info, + GString *result, +@@ -3193,6 +3221,7 @@ interpolate_replacement (const GMatchInfo *match_info, + InterpolationData *idata; + gchar *match; + ChangeCase change_case = CHANGE_CASE_NONE; ++ gboolean is_raw = (match_info != NULL && (match_info->regex->orig_compile_opts & G_REGEX_RAW)); + + for (list = data; list; list = list->next) + { +@@ -3200,10 +3229,10 @@ interpolate_replacement (const GMatchInfo *match_info, + switch (idata->type) + { + case REPL_TYPE_STRING: +- string_append (result, idata->text, &change_case); ++ string_append (result, idata->text, is_raw, &change_case); + break; + case REPL_TYPE_CHARACTER: +- g_string_append_c (result, CHANGE_CASE (idata->c, change_case)); ++ g_string_append_c (result, UTF8_CHANGE_CASE (idata->c, change_case)); + if (change_case & CHANGE_CASE_SINGLE_MASK) + change_case = CHANGE_CASE_NONE; + break; +@@ -3211,7 +3240,7 @@ interpolate_replacement (const GMatchInfo *match_info, + match = g_match_info_fetch (match_info, idata->num); + if (match) + { +- string_append (result, match, &change_case); ++ string_append (result, match, is_raw, &change_case); + g_free (match); + } + break; +@@ -3219,7 +3248,7 @@ interpolate_replacement (const GMatchInfo *match_info, + match = g_match_info_fetch_named (match_info, idata->text); + if (match) + { +- string_append (result, match, &change_case); ++ string_append (result, match, is_raw, &change_case); + g_free (match); + } + break; +diff --git a/glib/tests/regex.c b/glib/tests/regex.c +index d7a698e..bffb52a 100644 +--- a/glib/tests/regex.c ++++ b/glib/tests/regex.c +@@ -2529,6 +2529,58 @@ test_compiled_regex_after_jit_failure (void) + g_regex_unref (regex); + } + ++static void ++test_replace_raw_change_case (void) ++{ ++ GError *local_error = NULL; ++ GRegex *regex = NULL; ++ ++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3918"); ++ g_test_summary ("Test that case changes as part of a replacement are handled correctly in G_REGEX_RAW mode"); ++ ++ /* ++ * Match a multi-byte sequence in RAW mode. The pattern matches ++ * exactly 2 bytes. The subject contains a 4-byte UTF-8 lead (0xF4) ++ * followed by only one continuation byte, then NUL. ++ * ++ * The matched substring will be "\xf4\x80" (2 bytes, heap-allocated ++ * as 3-byte buffer with NUL). If the code regresses and tries to handle ++ * the replacement as UTF-8 then g_utf8_get_char() would see 0xF4 and try ++ * to read 4 bytes, going 1 byte past the NUL into OOB territory. ++ */ ++ regex = g_regex_new ("..", G_REGEX_RAW, 0, &local_error); ++ g_assert_no_error (local_error); ++ ++ /* ++ * Build a subject string with truncated UTF-8. ++ * \xF4 = 4-byte UTF-8 lead byte ++ * \x80 = continuation byte ++ * No 3rd/4th continuation bytes — the match is only 2 bytes. ++ * ++ * \U\0 = uppercase the entire match → triggers string_append() ++ * with case change on the 2-byte non-UTF-8 match. ++ */ ++ char subject[] = "\xf4\x80"; ++ char *result = g_regex_replace (regex, subject, -1, 0, "\\U\\0", 0, &local_error); ++ g_assert_no_error (local_error); ++ ++ g_clear_pointer (&result, g_free); ++ g_clear_pointer (®ex, g_regex_unref); ++ ++ /* ++ * Second variant: single-char case change \u with \0 backreference. ++ */ ++ regex = g_regex_new (".", G_REGEX_RAW, 0, &local_error); ++ g_assert_no_error (local_error); ++ ++ char subject2[] = "\xe6\xb0"; /* 3-byte UTF-8 lead, only 2 bytes */ ++ result = g_regex_replace (regex, subject2, -1, 0, "\\u\\0", 0, &local_error); ++ g_assert_no_error (local_error); ++ ++ g_clear_pointer (&result, g_free); ++ g_clear_pointer (®ex, g_regex_unref); ++} ++ + int + main (int argc, char *argv[]) + { +@@ -2550,6 +2602,7 @@ main (int argc, char *argv[]) + g_test_add_func ("/regex/jit-unsupported-matching", test_jit_unsupported_matching_options); + g_test_add_func ("/regex/unmatched-named-subpattern", test_unmatched_named_subpattern); + g_test_add_func ("/regex/compiled-regex-after-jit-failure", test_compiled_regex_after_jit_failure); ++ g_test_add_func ("/regex/replace-raw-change-case", test_replace_raw_change_case); + + /* TEST_NEW(pattern, compile_opts, match_opts) */ + TEST_NEW("[A-Z]+", G_REGEX_CASELESS | G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTBOL | G_REGEX_MATCH_PARTIAL); diff -Nru glib2.0-2.84.4/debian/patches/gregex-Fix-use-of-wrong-option-flags-set-for-checking-for.patch glib2.0-2.84.4/debian/patches/gregex-Fix-use-of-wrong-option-flags-set-for-checking-for.patch --- glib2.0-2.84.4/debian/patches/gregex-Fix-use-of-wrong-option-flags-set-for-checking-for.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gregex-Fix-use-of-wrong-option-flags-set-for-checking-for.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,90 @@ +From: Philip Withnall +Date: Tue, 31 Mar 2026 17:01:19 +0100 +Subject: gregex: Fix use of wrong option flags set for checking for + G_REGEX_RAW +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +`GRegex.compile_opts` contains the options using PCRE2’s flags; +`GRegex.orig_compile_opts` contains them in `GRegexCompileFlags` format, +and that’s what we want here. + +Spotted by linhlhq as #YWH-PGM9867-194. The suggested fix is theirs, and +the unit test is adapted from their report. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.86.5, commit:67775ba7e88a790c4639cca72e79317115f4a6f7 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3919 +--- + glib/gregex.c | 4 ++-- + glib/tests/regex.c | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 34 insertions(+), 2 deletions(-) + +diff --git a/glib/gregex.c b/glib/gregex.c +index ce269ef..f6d0266 100644 +--- a/glib/gregex.c ++++ b/glib/gregex.c +@@ -208,10 +208,10 @@ + + /* if the string is in UTF-8 use g_utf8_ functions, else use + * use just +/- 1. */ +-#define NEXT_CHAR(re, s) (((re)->compile_opts & G_REGEX_RAW) ? \ ++#define NEXT_CHAR(re, s) (((re)->orig_compile_opts & G_REGEX_RAW) ? \ + ((s) + 1) : \ + g_utf8_next_char (s)) +-#define PREV_CHAR(re, s) (((re)->compile_opts & G_REGEX_RAW) ? \ ++#define PREV_CHAR(re, s) (((re)->orig_compile_opts & G_REGEX_RAW) ? \ + ((s) - 1) : \ + g_utf8_prev_char (s)) + +diff --git a/glib/tests/regex.c b/glib/tests/regex.c +index bffb52a..44640b0 100644 +--- a/glib/tests/regex.c ++++ b/glib/tests/regex.c +@@ -2581,6 +2581,37 @@ test_replace_raw_change_case (void) + g_clear_pointer (®ex, g_regex_unref); + } + ++static void ++test_split_raw (void) ++{ ++ GError *local_error = NULL; ++ GRegex *regex = NULL; ++ char *subject = NULL; ++ char **tokens = NULL; ++ ++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3919"); ++ g_test_summary ("Test splitting a string in G_REGEX_RAW mode"); ++ ++ /* Empty pattern in RAW mode — matches at every position */ ++ regex = g_regex_new ("", G_REGEX_RAW, 0, &local_error); ++ g_assert_no_error (local_error); ++ ++ /* ++ * Subject: single continuation byte 0x80, heap-allocated. ++ * When split encounters empty match at position 0, if the code were to ++ * regress then PREV_CHAR would call g_utf8_prev_char(&string[0]), which ++ * would scan backwards past the allocation start. ++ */ ++ subject = g_strdup ("\x80"); ++ ++ tokens = g_regex_split_full (regex, subject, -1, 0, 0, 0, &local_error); ++ g_assert_no_error (local_error); ++ ++ g_strfreev (tokens); ++ g_free (subject); ++ g_regex_unref (regex); ++} ++ + int + main (int argc, char *argv[]) + { +@@ -2603,6 +2634,7 @@ main (int argc, char *argv[]) + g_test_add_func ("/regex/unmatched-named-subpattern", test_unmatched_named_subpattern); + g_test_add_func ("/regex/compiled-regex-after-jit-failure", test_compiled_regex_after_jit_failure); + g_test_add_func ("/regex/replace-raw-change-case", test_replace_raw_change_case); ++ g_test_add_func ("/regex/split-raw", test_split_raw); + + /* TEST_NEW(pattern, compile_opts, match_opts) */ + TEST_NEW("[A-Z]+", G_REGEX_CASELESS | G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTBOL | G_REGEX_MATCH_PARTIAL); diff -Nru glib2.0-2.84.4/debian/patches/gregex-Rename-the-compile_opts-members-to-clarify-their-t.patch glib2.0-2.84.4/debian/patches/gregex-Rename-the-compile_opts-members-to-clarify-their-t.patch --- glib2.0-2.84.4/debian/patches/gregex-Rename-the-compile_opts-members-to-clarify-their-t.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gregex-Rename-the-compile_opts-members-to-clarify-their-t.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,140 @@ +From: Philip Withnall +Date: Tue, 31 Mar 2026 17:11:56 +0100 +Subject: gregex: Rename the `compile_opts` members to clarify their types +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +It’s easy to confuse `compile_opts` (`PCRE2_*` flags) and +`orig_compile_opts` (`GRegexCompileFlags` flags). Rename them to +`pcre2_compile_opts` and `regex_compile_opts`, respectively, to make it +a bit less confusing. + +Unfortunately the type checker can’t help us here as the `PCRE2_*` flags +are defined by `pcre2` using `#define`s rather than an `enum`, so +`pcre2_compile_opts` has to stay as a `uint32_t` rather than a more +specific type. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.86.5, commit:3a76831c8a51c0e4d1e214a02760f98712916b3c +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3919 +--- + glib/gregex.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +diff --git a/glib/gregex.c b/glib/gregex.c +index f6d0266..cc263b8 100644 +--- a/glib/gregex.c ++++ b/glib/gregex.c +@@ -208,10 +208,10 @@ + + /* if the string is in UTF-8 use g_utf8_ functions, else use + * use just +/- 1. */ +-#define NEXT_CHAR(re, s) (((re)->orig_compile_opts & G_REGEX_RAW) ? \ ++#define NEXT_CHAR(re, s) (((re)->regex_compile_opts & G_REGEX_RAW) ? \ + ((s) + 1) : \ + g_utf8_next_char (s)) +-#define PREV_CHAR(re, s) (((re)->orig_compile_opts & G_REGEX_RAW) ? \ ++#define PREV_CHAR(re, s) (((re)->regex_compile_opts & G_REGEX_RAW) ? \ + ((s) - 1) : \ + g_utf8_prev_char (s)) + +@@ -246,8 +246,8 @@ struct _GRegex + gint ref_count; /* the ref count for the immutable part (atomic) */ + gchar *pattern; /* the pattern */ + pcre2_code *pcre_re; /* compiled form of the pattern */ +- uint32_t compile_opts; /* options used at compile time on the pattern, pcre2 values */ +- GRegexCompileFlags orig_compile_opts; /* options used at compile time on the pattern, gregex values */ ++ uint32_t pcre2_compile_opts; /* options used at compile time on the pattern, pcre2 values */ ++ GRegexCompileFlags regex_compile_opts; /* options used at compile time on the pattern, gregex values */ + uint32_t match_opts; /* pcre2 options used at match time on the regex */ + GRegexMatchFlags orig_match_opts; /* options used as default match options, gregex values */ + uint32_t jit_options; /* options which were enabled for jit compiler */ +@@ -827,7 +827,7 @@ match_info_new (const GRegex *regex, + match_info->matches = PCRE2_ERROR_NOMATCH; + match_info->pos = start_position; + match_info->match_opts = +- get_pcre2_match_options (match_options, regex->orig_compile_opts); ++ get_pcre2_match_options (match_options, regex->regex_compile_opts); + + pcre2_pattern_info (regex->pcre_re, PCRE2_INFO_CAPTURECOUNT, + &match_info->n_subpatterns); +@@ -907,7 +907,7 @@ enable_jit_with_match_options (GMatchInfo *match_info, + gint retval; + uint32_t old_jit_options, new_jit_options; + +- if (!(match_info->regex->orig_compile_opts & G_REGEX_OPTIMIZE)) ++ if (!(match_info->regex->regex_compile_opts & G_REGEX_OPTIMIZE)) + return JIT_STATUS_DISABLED; + + if (match_info->regex->jit_status == JIT_STATUS_DISABLED) +@@ -1509,7 +1509,7 @@ get_matched_substring_number (const GMatchInfo *match_info, + PCRE2_SPTR first, last; + guchar *entry; + +- if (!(match_info->regex->compile_opts & PCRE2_DUPNAMES)) ++ if (!(match_info->regex->pcre2_compile_opts & PCRE2_DUPNAMES)) + return pcre2_substring_number_from_name (match_info->regex->pcre_re, (PCRE2_SPTR8) name); + + /* This code is analogous to code from pcre2_substring.c: +@@ -1792,8 +1792,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS + regex->ref_count = 1; + regex->pattern = g_strdup (pattern); + regex->pcre_re = re; +- regex->compile_opts = pcre_compile_options; +- regex->orig_compile_opts = compile_options; ++ regex->pcre2_compile_opts = pcre_compile_options; ++ regex->regex_compile_opts = compile_options; + regex->match_opts = pcre_match_options; + regex->orig_match_opts = match_options; + +@@ -2042,7 +2042,7 @@ g_regex_get_compile_flags (const GRegex *regex) + g_return_val_if_fail (regex != NULL, 0); + + /* Preserve original G_REGEX_OPTIMIZE */ +- extra_flags = (regex->orig_compile_opts & G_REGEX_OPTIMIZE); ++ extra_flags = (regex->regex_compile_opts & G_REGEX_OPTIMIZE); + + /* Also include the newline options */ + pcre2_pattern_info (regex->pcre_re, PCRE2_INFO_NEWLINE, &info_value); +@@ -2075,7 +2075,7 @@ g_regex_get_compile_flags (const GRegex *regex) + break; + } + +- return g_regex_compile_flags_from_pcre2 (regex->compile_opts) | extra_flags; ++ return g_regex_compile_flags_from_pcre2 (regex->pcre2_compile_opts) | extra_flags; + } + + /** +@@ -2415,11 +2415,11 @@ g_regex_match_all_full (const GRegex *regex, + + newline_options = get_pcre2_newline_match_options (match_options); + if (!newline_options) +- newline_options = get_pcre2_newline_compile_options (regex->orig_compile_opts); ++ newline_options = get_pcre2_newline_compile_options (regex->regex_compile_opts); + + bsr_options = get_pcre2_bsr_match_options (match_options); + if (!bsr_options) +- bsr_options = get_pcre2_bsr_compile_options (regex->orig_compile_opts); ++ bsr_options = get_pcre2_bsr_compile_options (regex->regex_compile_opts); + + /* For PCRE2 we need to turn off PCRE2_NO_AUTO_POSSESS, which is an + * optimization for normal regex matching, but results in omitting some +@@ -2428,7 +2428,7 @@ g_regex_match_all_full (const GRegex *regex, + * DFA matching is rather niche, and very rarely used according to + * codesearch.debian.net, so don't bother caching the recompiled RE. */ + pcre_re = regex_compile (regex->pattern, +- regex->compile_opts | PCRE2_NO_AUTO_POSSESS, ++ regex->pcre2_compile_opts | PCRE2_NO_AUTO_POSSESS, + newline_options, bsr_options, error); + if (pcre_re == NULL) + return FALSE; +@@ -3221,7 +3221,7 @@ interpolate_replacement (const GMatchInfo *match_info, + InterpolationData *idata; + gchar *match; + ChangeCase change_case = CHANGE_CASE_NONE; +- gboolean is_raw = (match_info != NULL && (match_info->regex->orig_compile_opts & G_REGEX_RAW)); ++ gboolean is_raw = (match_info != NULL && (match_info->regex->regex_compile_opts & G_REGEX_RAW)); + + for (list = data; list; list = list->next) + { diff -Nru glib2.0-2.84.4/debian/patches/gvariant-Fix-an-off-by-one-error-in-an-offset-comparison.patch glib2.0-2.84.4/debian/patches/gvariant-Fix-an-off-by-one-error-in-an-offset-comparison.patch --- glib2.0-2.84.4/debian/patches/gvariant-Fix-an-off-by-one-error-in-an-offset-comparison.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/gvariant-Fix-an-off-by-one-error-in-an-offset-comparison.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,106 @@ +From: Philip Withnall +Date: Sun, 29 Mar 2026 19:10:41 +0100 +Subject: gvariant: Fix an off-by-one error in an offset comparison +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +This allows a single byte out-of-bounds read off the end of the +(potentially untrusted) byte array backing a `GVariant` when it’s +being checked for normal form. + +I can’t see how this could practically be exploited, but it’s certainly +a security bug as the `GVariant` normal form checking code is supposed +to be robust to malicious inputs. + +Spotted by linhlhq as #YWH-PGM9867-190, and fix and reproducer provided +by them too, thanks. Confirmed and turned into a unit test by me. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.86.5, commit:aa1cb87d56111ef989811e824f0ac77484cc997f +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3915 +Bug-CVE: CVE-2026-58010 +--- + glib/gvariant-serialiser.c | 2 +- + glib/tests/gvariant.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 49 insertions(+), 1 deletion(-) + +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index 08c2e21..bf2b24a 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -1250,7 +1250,7 @@ gvs_tuple_is_normal (GVariantSerialised value) + + while (offset & alignment) + { +- if (offset > value.size || value.data[offset] != '\0') ++ if (offset >= value.size || value.data[offset] != '\0') + return FALSE; + offset++; + } +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index 2644ec2..21997df 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -5770,6 +5770,52 @@ test_normal_checking_tuple_offsets5 (void) + g_variant_unref (variant); + } + ++/* This is a regression test that looping over the padding bytes in a short ++ * (non-normal) tuple doesn’t overflow the input data. ++ * ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/3915 */ ++static void ++test_normal_checking_tuple_offsets6 (void) ++{ ++ /* ++ * Type: (ynqiuxthdsog) — 12 members, first member 'y' (byte) has ++ * alignment 0, second 'n' (int16) has alignment 1. ++ * With 1 byte of data (0x28), after reading the first byte member, ++ * offset=1, alignment check for 'n' requires offset to be even, ++ * so the while loop checks value.data[1] — but size is only 1. ++ * ++ * Use heap allocation via GBytes so ASan reports heap-buffer-overflow. ++ */ ++ guint8 *heap_data = NULL; ++ GBytes *bytes = NULL; ++ const GVariantType *data_type = G_VARIANT_TYPE ("(ynqiuxthdsog)"); ++ GVariant *variant = NULL; ++ GVariant *normal_variant = NULL; ++ GVariant *expected = NULL; ++ ++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3915"); ++ ++ heap_data = g_malloc (1); ++ heap_data[0] = 0x28; ++ bytes = g_bytes_new_take (heap_data, 1); ++ ++ variant = g_variant_new_from_bytes (data_type, bytes, FALSE); ++ g_assert_nonnull (variant); ++ ++ g_assert_false (g_variant_is_normal_form (variant)); ++ ++ normal_variant = g_variant_get_normal_form (variant); ++ g_assert_nonnull (normal_variant); ++ ++ expected = g_variant_new_parsed ("(byte 0x28, int16 0, uint16 0, 0, uint32 0, int64 0, uint64 0, handle 0, 0.0, '', objectpath '/', signature '')"); ++ g_assert_cmpvariant (expected, variant); ++ g_assert_cmpvariant (expected, normal_variant); ++ ++ g_variant_unref (expected); ++ g_variant_unref (normal_variant); ++ g_variant_unref (variant); ++} ++ + /* Test that an otherwise-valid serialised GVariant is considered non-normal if + * its offset table entries are too wide. + * +@@ -6047,6 +6093,8 @@ main (int argc, char **argv) + test_normal_checking_tuple_offsets4); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets5", + test_normal_checking_tuple_offsets5); ++ g_test_add_func ("/gvariant/normal-checking/tuple-offsets6", ++ test_normal_checking_tuple_offsets6); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized", + test_normal_checking_tuple_offsets_minimal_sized); + g_test_add_func ("/gvariant/normal-checking/empty-object-path", diff -Nru glib2.0-2.84.4/debian/patches/series glib2.0-2.84.4/debian/patches/series --- glib2.0-2.84.4/debian/patches/series 2026-02-16 08:09:38.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/series 2026-08-07 09:33:15.000000000 +0000 @@ -31,3 +31,22 @@ CVE-2026-1489-2.patch CVE-2026-1489-3.patch CVE-2026-1489-4.patch +gvariant-Fix-an-off-by-one-error-in-an-offset-comparison.patch +gmarkup-Fix-potential-one-byte-overread-in-g_markup_escap.patch +gdatetime-Factor-out-a-couple-of-magic-constants.patch +gdatetime-Add-missing-range-validation-to-g_date_time_add.patch +gregex-Fix-case-changing-substitutions-with-G_REGEX_RAW.patch +gregex-Fix-use-of-wrong-option-flags-set-for-checking-for.patch +gregex-Rename-the-compile_opts-members-to-clarify-their-t.patch +giochannel-Fix-memcmp-off-the-end-of-the-buffer-with-long.patch +gkeyfile-Fix-a-one-byte-heap-under-read-with-g_key_file_g.patch +gdbusmessage-Fix-types-of-integer-arithmetic-in-message-l.patch +gdbusauthmechanismsha1-Validate-cookie-context.patch +gdbusauthmechanismsha1-Improve-validation-of-cookie-ID.patch +gdbusauthmechanism-Expose-client-reject-reason-as-a-new-v.patch +tests-Add-a-unit-test-for-GDBusAuthMechanismSha1-cookie-c.patch +gdbusauth-Limit-length-of-lines-read-from-client.patch +gdbusintrospection-Add-some-assertions-before-array-deref.patch +tests-Improve-D-Bus-introspection-test-paths.patch +gdbusintrospection-Fix-XML-parser-state-handling-for-node.patch +fuzzing-Add-a-fuzz-test-for-g_dbus_node_info_new_for_xml.patch diff -Nru glib2.0-2.84.4/debian/patches/tests-Add-a-unit-test-for-GDBusAuthMechanismSha1-cookie-c.patch glib2.0-2.84.4/debian/patches/tests-Add-a-unit-test-for-GDBusAuthMechanismSha1-cookie-c.patch --- glib2.0-2.84.4/debian/patches/tests-Add-a-unit-test-for-GDBusAuthMechanismSha1-cookie-c.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/tests-Add-a-unit-test-for-GDBusAuthMechanismSha1-cookie-c.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,211 @@ +From: Philip Withnall +Date: Tue, 28 Apr 2026 15:52:53 +0100 +Subject: tests: Add a unit test for GDBusAuthMechanismSha1 cookie context + parsing + +This checks for regressions in the fixes from the previous few commits. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.88.1, commit:0919301962291a712067ee0c5d273cc392f33277 +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3931 +--- + gio/tests/gdbus-auth-mechanism-sha1.c | 177 ++++++++++++++++++++++++++++++++++ + gio/tests/meson.build | 1 + + 2 files changed, 178 insertions(+) + create mode 100644 gio/tests/gdbus-auth-mechanism-sha1.c + +diff --git a/gio/tests/gdbus-auth-mechanism-sha1.c b/gio/tests/gdbus-auth-mechanism-sha1.c +new file mode 100644 +index 0000000..abcdb4e +--- /dev/null ++++ b/gio/tests/gdbus-auth-mechanism-sha1.c +@@ -0,0 +1,177 @@ ++/* GLib testing framework examples and tests ++ * ++ * Copyright (C) 2026 Philip Withnall ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ * ++ * Author: Philip Withnall ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "gdbus-tests.h" ++ ++#ifdef G_OS_UNIX ++#include ++#include ++#include ++#include ++#endif ++ ++#define GIO_COMPILATION 1 ++#include "gdbusauthmechanism.h" ++#include "gdbusauthmechanismsha1.h" ++ ++/* Vfunc wrappers copied from gdbusauthmechanism.c as they are not public. */ ++static gboolean ++dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism) ++{ ++ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->is_supported (mechanism); ++} ++ ++static GDBusAuthMechanismState ++dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism) ++{ ++ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_state (mechanism); ++} ++ ++static gchar * ++dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism, ++ GDBusConnectionFlags conn_flags, ++ size_t *out_initial_response_len) ++{ ++ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_initiate (mechanism, ++ conn_flags, ++ out_initial_response_len); ++} ++ ++static void ++dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism, ++ const char *data, ++ size_t data_len) ++{ ++ G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_receive (mechanism, data, data_len); ++} ++ ++static char * ++dbus_auth_mechanism_client_get_reject_reason (GDBusAuthMechanism *mechanism) ++{ ++ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_reject_reason (mechanism); ++} ++ ++static void ++dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism) ++{ ++ G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_shutdown (mechanism); ++} ++ ++static void ++test_server_challenge_validation (void) ++{ ++ const struct ++ { ++ const char *server_challenge; ++ const char *expected_reject_reason_prefix; ++ } ++ vectors[] = { ++ { "valid_context 123 456", "Problems looking up entry in keyring" }, ++ { "invalid/context 123 456", "Malformed cookie_context" }, ++ { "invalid.context 123 456", "Malformed cookie_context" }, ++ { " 123 456", "Malformed cookie_context" }, ++ { "😀 123 456", "Malformed cookie_context" }, ++ { "invalid\ncontext 123 456", "Malformed cookie_context" }, ++ { "invalid\rcontext 123 456", "Malformed cookie_context" }, ++ { "invalid\tcontext 123 456", "Malformed cookie_context" }, ++ { "invalid\\context 123 456", "Malformed cookie_context" }, ++ { "valid_context 456", "Malformed cookie_id" }, ++ { "valid_context 123notanumber 456", "Malformed cookie_id" }, ++ { "valid_context -1 456", "Malformed cookie_id" }, ++ { "valid_context 4294967296 456", "Malformed cookie_id" }, ++ { "valid_context 123 ", "Malformed data" }, ++ { "valid_context ", "Malformed data" }, ++ }; ++ GType mechanism_type; ++ GDBusConnection *connection = NULL; ++ ++ g_test_summary ("Test that GDBusAuthMechanismSha1 rejects various malformed server data lines"); ++ ++ /* Briefly connect to the actual bus to ensure the GDBusAuth mechanisms are ++ * all registered. */ ++ session_bus_up (); ++ ++ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); ++ g_assert_nonnull (connection); ++ g_clear_object (&connection); ++ ++ session_bus_down (); ++ ++ /* Check that we now have the type ID for GDBusAuthMechanismSha1 */ ++ mechanism_type = g_type_from_name ("GDBusAuthMechanismSha1"); ++ g_assert_cmpint (mechanism_type, !=, 0); ++ ++ for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++) ++ { ++ GDBusAuthMechanism *mechanism = NULL; ++ char *data = NULL; ++ size_t data_len = 0; ++ char *reject_reason = NULL; ++ ++ mechanism = g_object_new (mechanism_type, NULL); ++ ++ if (!dbus_auth_mechanism_is_supported (mechanism)) ++ { ++ g_test_skip ("Mechanism not supported"); ++ g_clear_object (&mechanism); ++ return; ++ } ++ ++ data = dbus_auth_mechanism_client_initiate (mechanism, ++ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, ++ &data_len); ++ g_free (data); ++ ++ dbus_auth_mechanism_client_data_receive (mechanism, vectors[i].server_challenge, strlen (vectors[i].server_challenge)); ++ ++ g_assert_cmpint (dbus_auth_mechanism_client_get_state (mechanism), ==, G_DBUS_AUTH_MECHANISM_STATE_REJECTED); ++ ++ reject_reason = dbus_auth_mechanism_client_get_reject_reason (mechanism); ++ g_assert_true (g_str_has_prefix (reject_reason, vectors[i].expected_reject_reason_prefix)); ++ g_free (reject_reason); ++ ++ dbus_auth_mechanism_client_shutdown (mechanism); ++ ++ g_clear_object (&mechanism); ++ } ++} ++ ++int ++main (int argc, ++ char *argv[]) ++{ ++ setlocale (LC_ALL, "C"); ++ ++ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); ++ ++ g_test_dbus_unset (); ++ ++ g_test_add_func ("/gdbus/auth-mechanism-sha1/server-challenge-validation", test_server_challenge_validation); ++ ++ return g_test_run (); ++} +diff --git a/gio/tests/meson.build b/gio/tests/meson.build +index 26091b6..ac3f37b 100644 +--- a/gio/tests/meson.build ++++ b/gio/tests/meson.build +@@ -458,6 +458,7 @@ if host_machine.system() != 'windows' + }, + 'fdo-notification-backend': {}, + 'gdbus-auth' : {'extra_sources' : extra_sources}, ++ 'gdbus-auth-mechanism-sha1': {'extra_sources' : extra_sources}, + 'gdbus-bz627724' : {'extra_sources' : extra_sources}, + 'gdbus-close-pending' : {'extra_sources' : extra_sources}, + 'gdbus-connection' : { diff -Nru glib2.0-2.84.4/debian/patches/tests-Improve-D-Bus-introspection-test-paths.patch glib2.0-2.84.4/debian/patches/tests-Improve-D-Bus-introspection-test-paths.patch --- glib2.0-2.84.4/debian/patches/tests-Improve-D-Bus-introspection-test-paths.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/patches/tests-Improve-D-Bus-introspection-test-paths.patch 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1,34 @@ +From: Philip Withnall +Date: Thu, 16 Apr 2026 15:17:48 +0100 +Subject: tests: Improve D-Bus introspection test paths + +This makes them more consistent with how test paths are meant to be +used. + +Signed-off-by: Philip Withnall +Origin: upstream, 2.89.0, commit:7b276f05f66cc0df609ba68f76b8a8f4cd5297cb +Bug: https://gitlab.gnome.org/GNOME/glib/-/work_items/3932 +Bug-Debian: https://bugs.debian.org/1141316 +--- + gio/tests/gdbus-introspection.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/gio/tests/gdbus-introspection.c b/gio/tests/gdbus-introspection.c +index cb99c94..f088475 100644 +--- a/gio/tests/gdbus-introspection.c ++++ b/gio/tests/gdbus-introspection.c +@@ -313,10 +313,10 @@ main (int argc, + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + +- g_test_add_func ("/gdbus/introspection-parser", test_introspection_parser); +- g_test_add_func ("/gdbus/introspection-generate", test_generate); +- g_test_add_func ("/gdbus/introspection-default-direction", test_default_direction); +- g_test_add_func ("/gdbus/introspection-extra-data", test_extra_data); ++ g_test_add_func ("/gdbus/introspection/parser", test_introspection_parser); ++ g_test_add_func ("/gdbus/introspection/generate", test_generate); ++ g_test_add_func ("/gdbus/introspection/default-direction", test_default_direction); ++ g_test_add_func ("/gdbus/introspection/extra-data", test_extra_data); + + ret = session_bus_run (); + diff -Nru glib2.0-2.84.4/debian/salsa-ci.yml glib2.0-2.84.4/debian/salsa-ci.yml --- glib2.0-2.84.4/debian/salsa-ci.yml 2026-02-13 11:51:32.000000000 +0000 +++ glib2.0-2.84.4/debian/salsa-ci.yml 2026-08-07 09:33:15.000000000 +0000 @@ -3,6 +3,9 @@ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml variables: + # There will be no new upstream releases from the 2.84.x branch + SALSA_CI_DISABLE_USCAN: 1 + # The test for GDir quite reasonably assumes that rewinddir() followed # by readdir() will start again with the same first item, but that isn't # the case in disorderfs diff -Nru glib2.0-2.84.4/debian/tests/manual/.gitignore glib2.0-2.84.4/debian/tests/manual/.gitignore --- glib2.0-2.84.4/debian/tests/manual/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.84.4/debian/tests/manual/.gitignore 2026-08-07 09:33:15.000000000 +0000 @@ -0,0 +1 @@ +/local-*.sources