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 <smcv@debian.org>  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 <andreas@fatal.se>  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 <pwithnall@gnome.org>
+ */
+
+#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 <withnall@endlessm.com>
  */
 
+#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&#x80;N" },
   { "N\xc2\x79N", "N\xc2\x79N" },
   { "N\xc2\x9fN", "N&#x9f;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 (&regex, 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 (&regex, 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);
