Package: release.debian.org
Severity: normal
Tags: trixie d-i
X-Debbugs-Cc: [email protected], [email protected],
[email protected]
Control: affects -1 + src:glib2.0
User: [email protected]
Usertags: pu
[ Reason ]
Fix non-urgent security issues
- CVE-2026-58010
- CVE-2026-58011
- CVE-2026-58012
- CVE-2026-58013
- CVE-2026-58014
- CVE-2026-58015
- CVE-2026-15588
- CVE-2026-58016
[ Impact ]
If not accepted, 8 no-DSA security issues remain unfixed. The proposed
patches also fix some issues that were reported upstream as potential
security vulnerabilities, but were classified as non-security because
they are only reachable if relevant APIs are used incorrectly (passing
non-UTF-8 to functions that require a valid UTF-8 argument).
[ Tests ]
A Debian 13 GNOME desktop and laptop still operate normally. New
automated test coverage is included for the fixed issues, and passes at
build-time and under autopkgtest.
[ Risks ]
All changes to upstream code are targeted, and are straightforward
backports of reviewed upstream changes. The patches applied cleanly
without conflicts, and the only backport-specific changes I had to make
were to add #include <stdint.h> in a few places (GLib ≥ 2.88 already
includes that header globally, but older GLib did not).
The only packaging change was to disable a failing Salsa-CI job (the
uscan check), which has no impact on the built binaries. This
stable-branch is old enough that upstream is no longer making releases
from it, so we will never need to run uscan anyway.
Any of the changes should be straightforward to revert if there's a
problem.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in (old)stable
(filtered to exclude redundant patch content)
[x] the issue is verified as fixed in unstable
[ Changes ]
... are described in the (large) changelog entry.
[ Other info ]
Needs d-i ack, for the GTK installer.
CVE-2026-16118 is not addressed here. It hasn't yet been fixed upstream
or in unstable either, and if it was up to me, I would be tempted to
dispute the CVE assignment and treat it as an ordinary bug (I'm having
difficulty thinking of a scenario where the files being parsed would be
attacker-controlled, without the attacker already having arbitrary code
execution some other way).
Thanks,
smcv
debdiff glib2.0_2.84.4-3~deb13u3.dsc glib2.0_2.84.4-3~deb13u4.dsc | filterdiff -p1 -x'debian/patches/*.patch'
---
diffstat for glib2.0-2.84.4 glib2.0-2.84.4
debian/changelog | 83 ++-
debian/patches/fuzzing-Add-a-fuzz-test-for-g_dbus_node_info_new_for_xml.patch | 68 ++
debian/patches/gdatetime-Add-missing-range-validation-to-g_date_time_add.patch | 66 ++
debian/patches/gdatetime-Factor-out-a-couple-of-magic-constants.patch | 55 ++
debian/patches/gdbusauth-Limit-length-of-lines-read-from-client.patch | 265 ++++++++++
debian/patches/gdbusauthmechanism-Expose-client-reject-reason-as-a-new-v.patch | 189 +++++++
debian/patches/gdbusauthmechanismsha1-Improve-validation-of-cookie-ID.patch | 56 ++
debian/patches/gdbusauthmechanismsha1-Validate-cookie-context.patch | 77 ++
debian/patches/gdbusintrospection-Add-some-assertions-before-array-deref.patch | 90 +++
debian/patches/gdbusintrospection-Fix-XML-parser-state-handling-for-node.patch | 89 +++
debian/patches/gdbusmessage-Fix-types-of-integer-arithmetic-in-message-l.patch | 141 +++++
debian/patches/giochannel-Fix-memcmp-off-the-end-of-the-buffer-with-long.patch | 131 ++++
debian/patches/gkeyfile-Fix-a-one-byte-heap-under-read-with-g_key_file_g.patch | 102 +++
debian/patches/gmarkup-Fix-potential-one-byte-overread-in-g_markup_escap.patch | 96 +++
debian/patches/gregex-Fix-case-changing-substitutions-with-G_REGEX_RAW.patch | 221 ++++++++
debian/patches/gregex-Fix-use-of-wrong-option-flags-set-for-checking-for.patch | 90 +++
debian/patches/gregex-Rename-the-compile_opts-members-to-clarify-their-t.patch | 140 +++++
debian/patches/gvariant-Fix-an-off-by-one-error-in-an-offset-comparison.patch | 106 ++++
debian/patches/series | 19
debian/patches/tests-Add-a-unit-test-for-GDBusAuthMechanismSha1-cookie-c.patch | 211 +++++++
debian/patches/tests-Improve-D-Bus-introspection-test-paths.patch | 34 +
debian/salsa-ci.yml | 3
debian/tests/manual/.gitignore | 1
fuzzing/fuzz_dbus_node_info_new_for_xml.c | 42 +
fuzzing/fuzz_key.c | 9
fuzzing/meson.build | 1
gio/gdbusauth.c | 44 +
gio/gdbusauthmechanism.c | 7
gio/gdbusauthmechanism.h | 2
gio/gdbusauthmechanismanon.c | 8
gio/gdbusauthmechanismexternal.c | 8
gio/gdbusauthmechanismsha1.c | 51 +
gio/gdbusintrospection.c | 10
gio/gdbusmessage.c | 39 -
gio/tests/gdbus-auth-mechanism-sha1.c | 177 ++++++
gio/tests/gdbus-auth.c | 119 ++++
gio/tests/gdbus-introspection.c | 41 +
gio/tests/gdbus-message.c | 16
gio/tests/meson.build | 1
glib/gdatetime.c | 13
glib/giochannel.c | 3
glib/gkeyfile.c | 2
glib/gmarkup.c | 8
glib/gregex.c | 87 ++-
glib/gvariant-serialiser.c | 2
glib/tests/gdatetime.c | 18
glib/tests/gvariant.c | 48 +
glib/tests/io-channel.c | 62 ++
glib/tests/keyfile.c | 23
glib/tests/markup-escape.c | 16
glib/tests/regex.c | 85 +++
51 files changed, 3189 insertions(+), 86 deletions(-)
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 10:33:15.000000000 +0100
@@ -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 <[email protected]> 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 <[email protected]> Mon, 16 Feb 2026 09:11:04 +0100
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 10:33:15.000000000 +0100
@@ -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/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 10:33:15.000000000 +0100
@@ -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 01:00:00.000000000 +0100
+++ glib2.0-2.84.4/debian/tests/manual/.gitignore 2026-08-07 10:33:15.000000000 +0100
@@ -0,0 +1 @@
+/local-*.sources
diff -Nru glib2.0-2.84.4/fuzzing/fuzz_dbus_node_info_new_for_xml.c glib2.0-2.84.4/fuzzing/fuzz_dbus_node_info_new_for_xml.c
--- glib2.0-2.84.4/fuzzing/fuzz_dbus_node_info_new_for_xml.c 1970-01-01 01:00:00.000000000 +0100
+++ glib2.0-2.84.4/fuzzing/fuzz_dbus_node_info_new_for_xml.c 2026-08-07 10:37:38.526547581 +0100
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "fuzz.h"
+
+int
+LLVMFuzzerTestOneInput (const unsigned char *data, size_t size)
+{
+ char *nul_terminated_data = NULL;
+ GDBusNodeInfo *node = NULL;
+ GError *local_error = NULL;
+
+ fuzz_set_logging_func ();
+
+ /* 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_assert ((node == NULL) == (local_error != NULL));
+
+ g_clear_pointer (&node, g_dbus_node_info_unref);
+ g_clear_error (&local_error);
+
+ return 0;
+}
diff -Nru glib2.0-2.84.4/fuzzing/fuzz_key.c glib2.0-2.84.4/fuzzing/fuzz_key.c
--- glib2.0-2.84.4/fuzzing/fuzz_key.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/fuzzing/fuzz_key.c 2026-08-07 10:37:38.000000000 +0100
@@ -26,11 +26,20 @@
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 -Nru glib2.0-2.84.4/fuzzing/meson.build glib2.0-2.84.4/fuzzing/meson.build
--- glib2.0-2.84.4/fuzzing/meson.build 2026-08-07 10:37:37.000000000 +0100
+++ glib2.0-2.84.4/fuzzing/meson.build 2026-08-07 10:37:38.000000000 +0100
@@ -25,6 +25,7 @@
'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/gio/gdbusauth.c glib2.0-2.84.4/gio/gdbusauth.c
--- glib2.0-2.84.4/gio/gdbusauth.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/gdbusauth.c 2026-08-07 10:37:38.000000000 +0100
@@ -260,6 +260,21 @@
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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 -Nru glib2.0-2.84.4/gio/gdbusauthmechanismanon.c glib2.0-2.84.4/gio/gdbusauthmechanismanon.c
--- glib2.0-2.84.4/gio/gdbusauthmechanismanon.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/gdbusauthmechanismanon.c 2026-08-07 10:37:38.000000000 +0100
@@ -56,7 +56,7 @@
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 @@
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 @@
}
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 -Nru glib2.0-2.84.4/gio/gdbusauthmechanism.c glib2.0-2.84.4/gio/gdbusauthmechanism.c
--- glib2.0-2.84.4/gio/gdbusauthmechanism.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/gdbusauthmechanism.c 2026-08-07 10:37:38.000000000 +0100
@@ -324,6 +324,13 @@
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 -Nru glib2.0-2.84.4/gio/gdbusauthmechanismexternal.c glib2.0-2.84.4/gio/gdbusauthmechanismexternal.c
--- glib2.0-2.84.4/gio/gdbusauthmechanismexternal.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/gdbusauthmechanismexternal.c 2026-08-07 10:37:38.000000000 +0100
@@ -64,7 +64,7 @@
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 @@
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 @@
}
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 -Nru glib2.0-2.84.4/gio/gdbusauthmechanism.h glib2.0-2.84.4/gio/gdbusauthmechanism.h
--- glib2.0-2.84.4/gio/gdbusauthmechanism.h 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/gdbusauthmechanism.h 2026-08-07 10:37:38.000000000 +0100
@@ -100,6 +100,7 @@
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 @@
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 -Nru glib2.0-2.84.4/gio/gdbusauthmechanismsha1.c glib2.0-2.84.4/gio/gdbusauthmechanismsha1.c
--- glib2.0-2.84.4/gio/gdbusauthmechanismsha1.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/gdbusauthmechanismsha1.c 2026-08-07 10:37:38.000000000 +0100
@@ -25,6 +25,7 @@
#include <string.h>
#include <fcntl.h>
#include <errno.h>
+#include <stdint.h>
#include <sys/types.h>
#include <glib/gstdio.h>
@@ -119,7 +120,7 @@
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,
@@ -172,12 +173,13 @@
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;
}
@@ -1128,12 +1130,11 @@
}
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);
@@ -1198,6 +1199,34 @@
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,
@@ -1206,7 +1235,7 @@
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;
@@ -1232,8 +1261,16 @@
}
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')
+ 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]);
@@ -1243,7 +1280,7 @@
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/gio/gdbusintrospection.c glib2.0-2.84.4/gio/gdbusintrospection.c
--- glib2.0-2.84.4/gio/gdbusintrospection.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/gdbusintrospection.c 2026-08-07 10:37:38.000000000 +0100
@@ -1096,6 +1096,7 @@
{
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 @@
{
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 @@
{
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 @@
{
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 @@
{
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 @@
{
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 @@
{
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 @@
{
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];
}
@@ -1258,7 +1266,7 @@
/* ---------------------------------------------------------------------------------------------------- */
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 -Nru glib2.0-2.84.4/gio/gdbusmessage.c glib2.0-2.84.4/gio/gdbusmessage.c
--- glib2.0-2.84.4/gio/gdbusmessage.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/gdbusmessage.c 2026-08-07 10:37:38.000000000 +0100
@@ -27,6 +27,7 @@
#include <string.h>
#include <errno.h>
+#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -2273,9 +2274,8 @@
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 @@
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_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 -Nru glib2.0-2.84.4/gio/tests/gdbus-auth.c glib2.0-2.84.4/gio/tests/gdbus-auth.c
--- glib2.0-2.84.4/gio/tests/gdbus-auth.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/tests/gdbus-auth.c 2026-08-07 10:37:38.000000000 +0100
@@ -263,6 +263,124 @@
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 @@
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/gio/tests/gdbus-auth-mechanism-sha1.c glib2.0-2.84.4/gio/tests/gdbus-auth-mechanism-sha1.c
--- glib2.0-2.84.4/gio/tests/gdbus-auth-mechanism-sha1.c 1970-01-01 01:00:00.000000000 +0100
+++ glib2.0-2.84.4/gio/tests/gdbus-auth-mechanism-sha1.c 2026-08-07 10:37:38.000000000 +0100
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Philip Withnall <[email protected]>
+ */
+
+#include <locale.h>
+#include <gio/gio.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include "gdbus-tests.h"
+
+#ifdef G_OS_UNIX
+#include <gio/gunixconnection.h>
+#include <gio/gnetworkingprivate.h>
+#include <gio/gunixsocketaddress.h>
+#include <gio/gunixfdlist.h>
+#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 -Nru glib2.0-2.84.4/gio/tests/gdbus-introspection.c glib2.0-2.84.4/gio/tests/gdbus-introspection.c
--- glib2.0-2.84.4/gio/tests/gdbus-introspection.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/tests/gdbus-introspection.c 2026-08-07 10:37:38.000000000 +0100
@@ -300,6 +300,38 @@
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 },
+ { "<node><interface name=\"I\"><method name=\"M\"><node><interface name=\"I2\"></interface></node></method>", G_MARKUP_ERROR_INVALID_CONTENT },
+ { "<node><interface name=\"I\"><signal name=\"S\"><node><interface name=\"I2\"><signal name=\"S2\"></signal></interface></node></signal>", G_MARKUP_ERROR_INVALID_CONTENT },
+ { "<node><interface name=\"I\"><property name=\"P\" type=\"s\" access=\"read\"><node><interface name=\"I2\"></interface></node></property>", G_MARKUP_ERROR_INVALID_CONTENT },
+ { "<node><interface name=\"I\"><method name=\"M\"><arg type=\"\"><node><interface name=\"I2\"><method name=\"M2\"></method></interface></node></arg>", 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
@@ -313,10 +345,11 @@
/* 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);
+ g_test_add_func ("/gdbus/introspection/invalid", test_invalid);
ret = session_bus_run ();
diff -Nru glib2.0-2.84.4/gio/tests/gdbus-message.c glib2.0-2.84.4/gio/tests/gdbus-message.c
--- glib2.0-2.84.4/gio/tests/gdbus-message.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/tests/gdbus-message.c 2026-08-07 10:37:38.000000000 +0100
@@ -185,6 +185,22 @@
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/gio/tests/meson.build glib2.0-2.84.4/gio/tests/meson.build
--- glib2.0-2.84.4/gio/tests/meson.build 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/gio/tests/meson.build 2026-08-07 10:37:38.000000000 +0100
@@ -458,6 +458,7 @@
},
'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/glib/gdatetime.c glib2.0-2.84.4/glib/gdatetime.c
--- glib2.0-2.84.4/glib/gdatetime.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/gdatetime.c 2026-08-07 10:37:38.000000000 +0100
@@ -99,7 +99,7 @@
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 @@
#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 @@
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 @@
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);
@@ -2075,7 +2078,9 @@
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 -Nru glib2.0-2.84.4/glib/giochannel.c glib2.0-2.84.4/glib/giochannel.c
--- glib2.0-2.84.4/glib/giochannel.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/giochannel.c 2026-08-07 10:37:38.000000000 +0100
@@ -1828,7 +1828,8 @@
{
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 -Nru glib2.0-2.84.4/glib/gkeyfile.c glib2.0-2.84.4/glib/gkeyfile.c
--- glib2.0-2.84.4/glib/gkeyfile.c 2026-08-07 10:37:37.000000000 +0100
+++ glib2.0-2.84.4/glib/gkeyfile.c 2026-08-07 10:37:38.000000000 +0100
@@ -2564,7 +2564,7 @@
}
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 -Nru glib2.0-2.84.4/glib/gmarkup.c glib2.0-2.84.4/glib/gmarkup.c
--- glib2.0-2.84.4/glib/gmarkup.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/gmarkup.c 2026-08-07 10:37:38.000000000 +0100
@@ -2200,7 +2200,7 @@
/* 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 @@
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 -Nru glib2.0-2.84.4/glib/gregex.c glib2.0-2.84.4/glib/gregex.c
--- glib2.0-2.84.4/glib/gregex.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/gregex.c 2026-08-07 10:37:38.000000000 +0100
@@ -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)->regex_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)->regex_compile_opts & G_REGEX_RAW) ? \
((s) - 1) : \
g_utf8_prev_char (s))
@@ -246,8 +246,8 @@
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->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 @@
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 @@
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 @@
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_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 @@
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 @@
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 @@
* 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;
@@ -3146,19 +3146,25 @@
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 @@
}
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 @@
InterpolationData *idata;
gchar *match;
ChangeCase change_case = CHANGE_CASE_NONE;
+ gboolean is_raw = (match_info != NULL && (match_info->regex->regex_compile_opts & G_REGEX_RAW));
for (list = data; list; list = list->next)
{
@@ -3200,10 +3229,10 @@
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 @@
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 @@
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 -Nru glib2.0-2.84.4/glib/gvariant-serialiser.c glib2.0-2.84.4/glib/gvariant-serialiser.c
--- glib2.0-2.84.4/glib/gvariant-serialiser.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/gvariant-serialiser.c 2026-08-07 10:37:38.000000000 +0100
@@ -1250,7 +1250,7 @@
while (offset & alignment)
{
- if (offset > value.size || value.data[offset] != '\0')
+ if (offset >= value.size || value.data[offset] != '\0')
return FALSE;
offset++;
}
diff -Nru glib2.0-2.84.4/glib/tests/gdatetime.c glib2.0-2.84.4/glib/tests/gdatetime.c
--- glib2.0-2.84.4/glib/tests/gdatetime.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/tests/gdatetime.c 2026-08-07 10:37:38.000000000 +0100
@@ -1141,6 +1141,24 @@
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/glib/tests/gvariant.c glib2.0-2.84.4/glib/tests/gvariant.c
--- glib2.0-2.84.4/glib/tests/gvariant.c 2026-08-07 10:37:37.000000000 +0100
+++ glib2.0-2.84.4/glib/tests/gvariant.c 2026-08-07 10:37:38.000000000 +0100
@@ -5770,6 +5770,52 @@
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 @@
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/glib/tests/io-channel.c glib2.0-2.84.4/glib/tests/io-channel.c
--- glib2.0-2.84.4/glib/tests/io-channel.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/tests/io-channel.c 2026-08-07 10:37:38.000000000 +0100
@@ -28,6 +28,8 @@
* Author: Philip Withnall <[email protected]>
*/
+#include <stdint.h>
+
#include <glib.h>
#include <glib/gstdio.h>
@@ -228,6 +230,65 @@
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 @@
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/glib/tests/keyfile.c glib2.0-2.84.4/glib/tests/keyfile.c
--- glib2.0-2.84.4/glib/tests/keyfile.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/tests/keyfile.c 2026-08-07 10:37:38.000000000 +0100
@@ -890,6 +890,28 @@
}
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)
{
GKeyFile *keyfile;
@@ -2011,6 +2033,7 @@
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/glib/tests/markup-escape.c glib2.0-2.84.4/glib/tests/markup-escape.c
--- glib2.0-2.84.4/glib/tests/markup-escape.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/tests/markup-escape.c 2026-08-07 10:37:38.000000000 +0100
@@ -37,6 +37,7 @@
{ "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 @@
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/glib/tests/regex.c glib2.0-2.84.4/glib/tests/regex.c
--- glib2.0-2.84.4/glib/tests/regex.c 2025-08-08 17:43:22.000000000 +0100
+++ glib2.0-2.84.4/glib/tests/regex.c 2026-08-07 10:37:38.000000000 +0100
@@ -2529,6 +2529,89 @@
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);
+}
+
+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[])
{
@@ -2550,6 +2633,8 @@
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);
+ 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);