[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1111600: bookworm-pu: package glib2.0/2.74.6-2+deb12u7



Package: release.debian.org
Severity: normal
Tags: bookworm d-i
X-Debbugs-Cc: glib2.0@packages.debian.org, debian-boot@lists.debian.org
Control: affects -1 + src:glib2.0
Control: block -1 by 1111470
User: release.debian.org@packages.debian.org
Usertags: pu

[ Reason ]

Avoid triggering #1065022, #1110696 in upgrades from bookworm to trixie.

Fix no-dsa CVEs #1104930, #1110640.

[ Impact ]

#1065022, #1110696 are upgrade issues: when bookworm's libglib2.0-0 is 
purged, its postrm deletes files that trixie's replacement 
libglib2.0-0t64 still needs. The impact is most GLib/GTK apps crashing 
out with a fatal error, until libglib2.0-0t64 is dpkg-reconfigure'd or 
reinstalled. We already work around #1065022 on the trixie side, and I'm 
proposing a similar workaround for #1110696 in trixie-pu bug #1111470, 
but it would be better if bookworm's libglib2.0-0.postrm was safer as 
well.

In particular, old versions of the postrm can hang around indefinitely 
due to the existence of the removed-but-not-purged state, so it would be 
good if we can make an attempt to fix this retroactively.

The two CVEs are unlikely to be exploitable in practice, but the 
worst-case-scenario impact if we turn out to be wrong about that is 
arbitrary code execution.

[ Tests ]

In general: autopkgtests are relatively extensive and all pass, except 
for memory-monitor-dbus which does not always pass but is already 
flagged as flaky (there are some known race conditions in that one, 
fixed upstream in a later version but not a high priority to backport). 
A GNOME laptop still works normally with the proposed version.

I tested #1065022, #1110696 with the manual test script 
debian/tests/manual/1065022.sh, as included in the trixie package 
proposed in #1111470:

- put proposed bookworm packages (only) in /path/to/proposed/debs
  (both amd64 and i386 are required)
- run "dpkg-scanpackages --multiversion . > Packages" in that directory
- podman run --rm -it \
      -v /path/to/glib:/mnt/glib:ro -w /mnt/glib \
      -v /path/to/proposed/debs:/mnt/bookworm:ro \
      debian:bookworm-slim debian/tests/manual/1065022.sh
- then repeat, adding argument "1110696"
- exit status should be 0 in both cases, stderr ends with "+ exit 0"

and these pass, even without updating the packages in trixie.

The new autopkgtest debian/patches/1065022-futureproofing also passes 
(tested in autopkgtest-virt-qemu and autopkgtest-virt-lxc on amd64).

The CVEs have no specific test coverage.

[ Risks ]

It's a key package in all desktop environments.

The upstream changes are narrowly-targeted and only fix specific bugs.

The downstream changes are not strictly minimal: I structured them to be 
as easy as possible to review, even if that means a few more lines of 
code. The only differences between the proposed 
debian/libglib2.0-0.postrm.in, and the debian/libglib2.0-0t64.postrm 
in unstable (and proposed for trixie) are:

- unstable uses debhelper's #DEB_HOST_MULTIARCH# substitution, but to
  minimize regression risk this proposed bookworm update is still doing
  its own substitution of #MULTIARCH# using sed;
- some differences in comments to reflect the older package name

Unlike the trixie package, the bookworm package does not need to go 
behind debhelper's back to fix up older packages, so the changes are 
simpler here.

[ 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
  [x] the issue is verified as fixed in unstable

[ Changes ]

All changes in debian/patches/ (and glib/) are upstream commits 
to fix the two CVEs. They cherry-picked cleanly from 2.84.x.

debian/patches/glib-gfileutils.c-use-64-bits-for-value-in-get_tmp_file.patch 
was a past bug fix related to what was later reported as CVE-2025-7039. 
Cherry-picking it allows the fix for the CVE, 
debian/patches/gfileutils-fix-computation-of-temporary-file-name.patch, 
to apply cleanly.

debian/patches/gstring-carefully-handle-gssize-parameters.patch was the 
original attempt to fix CVE-2025-4373, but had an important omission, 
fixed by debian/patches/gstring-Make-len_unsigned-unsigned.patch.

All other changes are for #1065022/#1110696. 
debian/patches/1065022-futureproofing is an automated test for this, 
backported from unstable; it's marked as flaky as a precaution because 
it relies on implementation details and might regress in future, but in 
practice it does pass.

[ Other info ]

This will need a d-i ack for the udeb, used in the graphical installer.
diffstat for glib2.0-2.74.6 glib2.0-2.74.6

 debian/changelog                                                             |   40 ++
 debian/libglib2.0-0.postrm.in                                                |   89 +++++-
 debian/patches/gfileutils-fix-computation-of-temporary-file-name.patch       |   42 +++
 debian/patches/glib-gfileutils.c-use-64-bits-for-value-in-get_tmp_file.patch |   40 ++
 debian/patches/gstring-Make-len_unsigned-unsigned.patch                      |   25 +
 debian/patches/gstring-carefully-handle-gssize-parameters.patch              |  119 ++++++++
 debian/patches/series                                                        |    4 
 debian/tests/1065022-futureproofing                                          |  137 ++++++++++
 debian/tests/control                                                         |    4 
 glib/gfileutils.c                                                            |    8 
 glib/gstring.c                                                               |   36 +-
 11 files changed, 515 insertions(+), 29 deletions(-)

diff -Nru glib2.0-2.74.6/debian/changelog glib2.0-2.74.6/debian/changelog
--- glib2.0-2.74.6/debian/changelog	2025-04-12 14:52:16.000000000 +0100
+++ glib2.0-2.74.6/debian/changelog	2025-08-18 09:27:51.000000000 +0100
@@ -1,3 +1,43 @@
+glib2.0 (2.74.6-2+deb12u7) bookworm; urgency=medium
+
+  * d/p/gstring-carefully-handle-gssize-parameters.patch,
+    d/p/gstring-Make-len_unsigned-unsigned.patch:
+    Add patches from upstream to fix a buffer underflow in GString.
+    This could cause a memory overwrite if a program handles extremely large
+    text strings of an attacker-controlled length. The required string length
+    would be close to 2 GiB on 32-bit and the bug is not believed to be
+    practically feasible to exploit on 64-bit. (CVE-2025-4373)
+    (Closes: #1104930)
+  * d/p/glib-gfileutils.c-use-64-bits-for-value-in-get_tmp_file.patch,
+    d/p/gfileutils-fix-computation-of-temporary-file-name.patch:
+    Add patches from upstream to fix a buffer underflow in get_tmp_file().
+    This is used in g_mkstemp(), g_mkdtemp() and similar functions, and
+    could cause a crash or possibly arbitrary file overwrites (believed to
+    be unlikely to be exploitable in practice) if a long-running program
+    creates more than 2 billion temporary files. (CVE-2025-7039)
+    (Closes: #1110640)
+  * d/libglib2.0-0.postrm.in:
+    Rewrite postrm for safer upgrade behaviour, based on the version
+    in unstable and proposed for inclusion in trixie:
+    - Only remove giomodule.cache during purge, not during remove.
+      This matches the behaviour of gschemas.compiled and avoids a window
+      between old-postrm and new-postinst during which giomodule.cache is
+      missing, breaking applications that need GIO modules.
+    - Don't remove gschemas.compiled or giomodule.cache during purge
+      if there is evidence that they might still be needed
+      (Closes: #1065022, #1110696):
+      + don't remove them if ${libdir}/glib-2.0 still exists, for example
+        provided by libglib2.0-0t64 after upgrading to trixie;
+      + don't remove gschemas.compiled if at least one GSettings schema
+        still exists;
+      + don't remove giomodule.cache if at least one GIO module still exists
+    - Refactoring to support the above
+  * d/tests/1065022-futureproofing:
+    Add a test for #1065022, modified from the version in unstable and
+    proposed for inclusion in trixie
+
+ -- Simon McVittie <smcv@debian.org>  Mon, 18 Aug 2025 09:27:51 +0100
+
 glib2.0 (2.74.6-2+deb12u6) bookworm; urgency=medium
 
   * Non-maintainer upload.
diff -Nru glib2.0-2.74.6/debian/libglib2.0-0.postrm.in glib2.0-2.74.6/debian/libglib2.0-0.postrm.in
--- glib2.0-2.74.6/debian/libglib2.0-0.postrm.in	2024-11-14 09:42:34.000000000 +0000
+++ glib2.0-2.74.6/debian/libglib2.0-0.postrm.in	2025-08-18 09:27:51.000000000 +0100
@@ -1,21 +1,86 @@
 #! /bin/sh
+# Debian Policy §10.4 says /bin/sh has a superset of POSIX functionality
+# shellcheck disable=SC3043
+
 set -e
 
 #DEBHELPER#
 
-case "$1" in
-    (remove|purge)
-        if [ -d /usr/lib/#MULTIARCH#/gio/modules ]; then
-            # Purge the cache
-            rm -f /usr/lib/#MULTIARCH#/gio/modules/giomodule.cache
-            rmdir -p --ignore-fail-on-non-empty /usr/lib/#MULTIARCH#/gio/modules
+clean_up_giomodule_cache ()
+{
+    local multiarch="#MULTIARCH#"
+    local modules="/usr/lib/${multiarch}/gio/modules"
+    local iter
+
+    if ! [ -d "$modules" ]; then
+        return 0
+    fi
+
+    # Don't remove giomodule.cache if libglib2.0-0 is replaced
+    # by some other ABI variant of essentially the same library
+    # (for example libglib2.0-0t64 in trixie), to avoid causing
+    # <https://bugs.debian.org/1065022>.
+    #
+    # This implementation is based on the assumption that any GLib
+    # version that still uses ${libdir}/gio/modules/giomodule.cache
+    # will also continue to ship ${libdir}/glib-2.0.
+    if [ -d "/usr/lib/${multiarch}/glib-2.0" ]; then
+        return 0
+    fi
+
+    # As an additional safety-catch, don't remove giomodule.cache if
+    # there is at least one module that should have been listed in it.
+    for iter in "$modules"/*.so; do
+        if [ -e "$iter" ]; then
+            echo "$0: not removing $modules/giomodule.cache because $iter still exists" >&2
+            return 0
         fi
+    done
+
+    rm -f "$modules/giomodule.cache"
+    rmdir -p --ignore-fail-on-non-empty "$modules"
+}
+
+clean_up_gsettings_schemas ()
+{
+    local schemas="/usr/share/glib-2.0/schemas"
+    local iter
+
+    if ! [ -d "$schemas" ]; then
+        return 0
+    fi
+
+    # Similarly, instead of using $DPKG_MAINTSCRIPT_PACKAGE_REFCOUNT, only
+    # remove gschemas.compiled if GLib has completely gone away - not just
+    # libglib2.0-0, but any future ABI variant like libglib2.0-0t64.
+    #
+    # This implementation is based on the assumption that any GLib
+    # version that still uses ${datadir}/glib-2.0/schemas
+    # will also continue to ship ${libdir}/glib-2.0.
+    for iter in /usr/lib/*/glib-2.0; do
+        if [ -e "$iter" ]; then
+            return 0
+        fi
+    done
+
+    # As an additional safety-catch, don't remove gschemas.compiled if
+    # there is at least one schema that should have been listed in it.
+    for iter in "$schemas"/*.xml; do
+        if [ -e "$iter" ]; then
+            echo "$0: not removing $schemas/gschemas.compiled because $iter still exists" >&2
+            return 0
+        fi
+    done
+
+    rm -f "$schemas/gschemas.compiled"
+    rmdir -p --ignore-fail-on-non-empty "$schemas"
+}
+
+case "$1" in
+    (purge)
+        clean_up_giomodule_cache
+        clean_up_gsettings_schemas
         ;;
 esac
 
-if [ "$1" = purge ] && [ -d /usr/share/glib-2.0/schemas ] && [ "$DPKG_MAINTSCRIPT_PACKAGE_REFCOUNT" = 1 ]; then
-    # This is the last multiarch variant to be removed, so drop the
-    # architecture-independent compiled schemas
-    rm -f /usr/share/glib-2.0/schemas/gschemas.compiled
-    rmdir -p --ignore-fail-on-non-empty /usr/share/glib-2.0/schemas
-fi
+# vim:set sw=4 sts=4 et:
diff -Nru glib2.0-2.74.6/debian/patches/gfileutils-fix-computation-of-temporary-file-name.patch glib2.0-2.74.6/debian/patches/gfileutils-fix-computation-of-temporary-file-name.patch
--- glib2.0-2.74.6/debian/patches/gfileutils-fix-computation-of-temporary-file-name.patch	1970-01-01 01:00:00.000000000 +0100
+++ glib2.0-2.74.6/debian/patches/gfileutils-fix-computation-of-temporary-file-name.patch	2025-08-18 09:27:51.000000000 +0100
@@ -0,0 +1,42 @@
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Tue, 1 Jul 2025 10:58:07 -0500
+Subject: gfileutils: fix computation of temporary file name
+
+We need to ensure that the value we use to index into the letters array
+is always positive.
+
+Origin: upstream, 2.84.4, commit:8f4da99bf2f112b8e4329d8c44b6ab5dea467cb1
+Origin: upstream, 2.85.2, commit:61e963284889ddb4544e6f1d5261c16120f6fcc3
+Bug: https://gitlab.gnome.org/GNOME/glib/-/issues/3716
+Bug-CVE: CVE-2025-7039
+Bug-Debian: https://bugs.debian.org/1110640
+---
+ glib/gfileutils.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/glib/gfileutils.c b/glib/gfileutils.c
+index 22c04e1..28b424a 100644
+--- a/glib/gfileutils.c
++++ b/glib/gfileutils.c
+@@ -1483,9 +1483,9 @@ get_tmp_file (gchar            *tmpl,
+   static const char letters[] =
+     "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+   static const int NLETTERS = sizeof (letters) - 1;
+-  gint64 value;
+-  gint64 now_us;
+-  static int counter = 0;
++  guint64 value;
++  guint64 now_us;
++  static guint counter = 0;
+ 
+   g_return_val_if_fail (tmpl != NULL, -1);
+ 
+@@ -1504,7 +1504,7 @@ get_tmp_file (gchar            *tmpl,
+ 
+   for (count = 0; count < 100; value += 7777, ++count)
+     {
+-      gint64 v = value;
++      guint64 v = value;
+ 
+       /* Fill in the random bits.  */
+       XXXXXX[0] = letters[v % NLETTERS];
diff -Nru glib2.0-2.74.6/debian/patches/glib-gfileutils.c-use-64-bits-for-value-in-get_tmp_file.patch glib2.0-2.74.6/debian/patches/glib-gfileutils.c-use-64-bits-for-value-in-get_tmp_file.patch
--- glib2.0-2.74.6/debian/patches/glib-gfileutils.c-use-64-bits-for-value-in-get_tmp_file.patch	1970-01-01 01:00:00.000000000 +0100
+++ glib2.0-2.74.6/debian/patches/glib-gfileutils.c-use-64-bits-for-value-in-get_tmp_file.patch	2025-08-18 09:27:51.000000000 +0100
@@ -0,0 +1,40 @@
+From: Alexander Kanavin <alex@linutronix.de>
+Date: Tue, 22 Aug 2023 19:57:48 +0200
+Subject: glib/gfileutils.c: use 64 bits for value in get_tmp_file()
+
+On 32 bit systems 'long' value will overflow in 2038 and become negative.
+As it is used to index into letters array, and % operation preserves signs,
+data corruption will then occur.
+
+[This change makes the patch for CVE-2025-7039 apply cleanly -smcv]
+
+Signed-off-by: Alexander Kanavin <alex@linutronix.de>
+Origin: upstream, 2.77.3, commit:285db475ecaa4d2cc39ce326b4c63aacb87ca6ad
+Origin: upstream, 2.76.5, commit:b4d60ba1367f15843577d4363b32fb16847b9582
+Bug-CVE: CVE-2025-7039
+---
+ glib/gfileutils.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/glib/gfileutils.c b/glib/gfileutils.c
+index 722575e..22c04e1 100644
+--- a/glib/gfileutils.c
++++ b/glib/gfileutils.c
+@@ -1483,7 +1483,7 @@ get_tmp_file (gchar            *tmpl,
+   static const char letters[] =
+     "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+   static const int NLETTERS = sizeof (letters) - 1;
+-  glong value;
++  gint64 value;
+   gint64 now_us;
+   static int counter = 0;
+ 
+@@ -1504,7 +1504,7 @@ get_tmp_file (gchar            *tmpl,
+ 
+   for (count = 0; count < 100; value += 7777, ++count)
+     {
+-      glong v = value;
++      gint64 v = value;
+ 
+       /* Fill in the random bits.  */
+       XXXXXX[0] = letters[v % NLETTERS];
diff -Nru glib2.0-2.74.6/debian/patches/gstring-carefully-handle-gssize-parameters.patch glib2.0-2.74.6/debian/patches/gstring-carefully-handle-gssize-parameters.patch
--- glib2.0-2.74.6/debian/patches/gstring-carefully-handle-gssize-parameters.patch	1970-01-01 01:00:00.000000000 +0100
+++ glib2.0-2.74.6/debian/patches/gstring-carefully-handle-gssize-parameters.patch	2025-08-18 09:27:51.000000000 +0100
@@ -0,0 +1,119 @@
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Mon, 28 Apr 2025 16:03:08 +0000
+Subject: gstring: carefully handle gssize parameters
+
+Wherever we use gssize to allow passing -1, we need to ensure we don't
+overflow the value by assigning a gsize to it without checking if the
+size exceeds the maximum gssize. The safest way to do this is to just
+use normal gsize everywhere instead and use gssize only for the
+parameter.
+
+Our computers don't have enough RAM to write tests for this. I tried
+forcing string->len to high values for test purposes, but this isn't
+valid and will just cause out of bounds reads/writes due to
+string->allocated_len being unexpectedly small, so I don't think we can
+test this easily.
+
+(cherry picked from commit cc647f9e46d55509a93498af19659baf9c80f2e3)
+
+Co-authored-by: Michael Catanzaro <mcatanzaro@redhat.com>
+Bug: https://gitlab.gnome.org/GNOME/glib/-/issues/3677
+Bug-CVE: CVE-2025-4373
+Bug-Debian: https://bugs.debian.org/1104930
+Origin: upstream, 2.84.2, commit:a47dc889463d73dd47ad428ac217e3d84f28e242
+---
+ glib/gstring.c | 36 +++++++++++++++++++++++-------------
+ 1 file changed, 23 insertions(+), 13 deletions(-)
+
+diff --git a/glib/gstring.c b/glib/gstring.c
+index 6abb70b..1a79759 100644
+--- a/glib/gstring.c
++++ b/glib/gstring.c
+@@ -426,8 +426,9 @@ g_string_insert_len (GString     *string,
+     return string;
+ 
+   if (len < 0)
+-    len = strlen (val);
+-  len_unsigned = len;
++    len_unsigned = strlen (val);
++  else
++    len_unsigned = len;
+ 
+   if (pos < 0)
+     pos_unsigned = string->len;
+@@ -725,10 +726,12 @@ g_string_insert_c (GString *string,
+   g_string_maybe_expand (string, 1);
+ 
+   if (pos < 0)
+-    pos = string->len;
++    pos_unsigned = string->len;
+   else
+-    g_return_val_if_fail ((gsize) pos <= string->len, string);
+-  pos_unsigned = pos;
++    {
++      pos_unsigned = pos;
++      g_return_val_if_fail (pos_unsigned <= string->len, string);
++    }
+ 
+   /* If not just an append, move the old stuff */
+   if (pos_unsigned < string->len)
+@@ -761,6 +764,7 @@ g_string_insert_unichar (GString  *string,
+                          gssize    pos,
+                          gunichar  wc)
+ {
++  gsize pos_unsigned;
+   gint charlen, first, i;
+   gchar *dest;
+ 
+@@ -802,15 +806,18 @@ g_string_insert_unichar (GString  *string,
+   g_string_maybe_expand (string, charlen);
+ 
+   if (pos < 0)
+-    pos = string->len;
++    pos_unsigned = string->len;
+   else
+-    g_return_val_if_fail ((gsize) pos <= string->len, string);
++    {
++      pos_unsigned = pos;
++      g_return_val_if_fail (pos_unsigned <= string->len, string);
++    }
+ 
+   /* If not just an append, move the old stuff */
+-  if ((gsize) pos < string->len)
+-    memmove (string->str + pos + charlen, string->str + pos, string->len - pos);
++  if (pos_unsigned < string->len)
++    memmove (string->str + pos_unsigned + charlen, string->str + pos_unsigned, string->len - pos_unsigned);
+ 
+-  dest = string->str + pos;
++  dest = string->str + pos_unsigned;
+   /* Code copied from g_unichar_to_utf() */
+   for (i = charlen - 1; i > 0; --i)
+     {
+@@ -868,6 +875,7 @@ g_string_overwrite_len (GString     *string,
+                         const gchar *val,
+                         gssize       len)
+ {
++  gssize len_unsigned;
+   gsize end;
+ 
+   g_return_val_if_fail (string != NULL, NULL);
+@@ -879,14 +887,16 @@ g_string_overwrite_len (GString     *string,
+   g_return_val_if_fail (pos <= string->len, string);
+ 
+   if (len < 0)
+-    len = strlen (val);
++    len_unsigned = strlen (val);
++  else
++    len_unsigned = len;
+ 
+-  end = pos + len;
++  end = pos + len_unsigned;
+ 
+   if (end > string->len)
+     g_string_maybe_expand (string, end - string->len);
+ 
+-  memcpy (string->str + pos, val, len);
++  memcpy (string->str + pos, val, len_unsigned);
+ 
+   if (end > string->len)
+     {
diff -Nru glib2.0-2.74.6/debian/patches/gstring-Make-len_unsigned-unsigned.patch glib2.0-2.74.6/debian/patches/gstring-Make-len_unsigned-unsigned.patch
--- glib2.0-2.74.6/debian/patches/gstring-Make-len_unsigned-unsigned.patch	1970-01-01 01:00:00.000000000 +0100
+++ glib2.0-2.74.6/debian/patches/gstring-Make-len_unsigned-unsigned.patch	2025-08-18 09:27:51.000000000 +0100
@@ -0,0 +1,25 @@
+From: Peter Bloomfield <peterbloomfield@bellsouth.net>
+Date: Fri, 11 Apr 2025 05:52:33 +0000
+Subject: gstring: Make len_unsigned unsigned
+
+Bug: https://gitlab.gnome.org/GNOME/glib/-/issues/3677
+Bug-CVE: CVE-2025-4373
+Bug-Debian: https://bugs.debian.org/1104930
+Origin: upstream, 2.84.2, commit:f32f4aea514e39086a2627e9483d841c9eeb9bc3
+---
+ glib/gstring.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/glib/gstring.c b/glib/gstring.c
+index 1a79759..4cba302 100644
+--- a/glib/gstring.c
++++ b/glib/gstring.c
+@@ -875,7 +875,7 @@ g_string_overwrite_len (GString     *string,
+                         const gchar *val,
+                         gssize       len)
+ {
+-  gssize len_unsigned;
++  gsize len_unsigned;
+   gsize end;
+ 
+   g_return_val_if_fail (string != NULL, NULL);
diff -Nru glib2.0-2.74.6/debian/patches/series glib2.0-2.74.6/debian/patches/series
--- glib2.0-2.74.6/debian/patches/series	2025-04-12 14:52:16.000000000 +0100
+++ glib2.0-2.74.6/debian/patches/series	2025-08-18 09:27:51.000000000 +0100
@@ -49,3 +49,7 @@
 0003-gdatetime-Track-timezone-length-as-an-unsigned-size_.patch
 0004-gdatetime-Factor-out-some-string-pointer-arithmetic.patch
 0005-gdatetime-Factor-out-an-undersized-variable.patch
+gstring-carefully-handle-gssize-parameters.patch
+gstring-Make-len_unsigned-unsigned.patch
+glib-gfileutils.c-use-64-bits-for-value-in-get_tmp_file.patch
+gfileutils-fix-computation-of-temporary-file-name.patch
diff -Nru glib2.0-2.74.6/debian/tests/1065022-futureproofing glib2.0-2.74.6/debian/tests/1065022-futureproofing
--- glib2.0-2.74.6/debian/tests/1065022-futureproofing	1970-01-01 01:00:00.000000000 +0100
+++ glib2.0-2.74.6/debian/tests/1065022-futureproofing	2025-08-18 09:27:51.000000000 +0100
@@ -0,0 +1,137 @@
+#!/bin/sh
+# Copyright 2024 Simon McVittie
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# Ensure that if the content of libglib2.0-0 is taken over by some
+# other package like libglib2.0-0t64 (in this test it is named
+# libglib2.0-0xyz), then that other package will not trigger
+# #1065022.
+
+set -eux
+
+export DEBIAN_FRONTEND=noninteractive
+n=0
+failed=0
+binary_package="libglib2.0-0"
+future_binary_package="libglib2.0-0xyz"
+srcdir="$(pwd)"
+tmpdir="$(mktemp -d)"
+cd "$tmpdir"
+
+# Machine-readable TAP on fd 3, human-readable diagnostics on fds 1 and 2
+exec 3>&1 >&2
+
+assert () {
+    n=$(( n + 1 ))
+
+    if "$@"; then
+        echo "ok $n - $*" >&3
+    else
+        echo "not ok $n - $* exit status $?" >&3
+        failed=1
+    fi
+}
+
+assert_not () {
+    n=$(( n + 1 ))
+
+    if ! "$@"; then
+        echo "ok $n - unsuccessful as expected: $*" >&3
+    else
+        echo "not ok $n - should not have succeeded: $*" >&3
+        failed=1
+    fi
+}
+
+# Add a deb822-formatted apt source at this location if you are testing a
+# locally-built glib2.0 before upload
+if [ -e "$srcdir/debian/tests/manual/local-1065022.sources" ]; then
+    install -m644 -t /etc/apt/sources.list.d/ -D \
+        "$srcdir/debian/tests/manual/local-1065022.sources"
+fi
+
+# For more convenient manual testing
+if ! dpkg-query -W dpkg-repack; then
+    apt-get -y update
+    apt-get -y install "$binary_package"
+    apt-get -y install dconf-gsettings-backend dpkg-repack gsettings-desktop-schemas
+fi
+
+# This assumes that libglib2.0-0 has at least one Breaks but no Provides
+# or Replaces, and will need to be adjusted if that assumption is broken in
+# the future for whatever reason.
+dpkg-query -s "$binary_package"
+# The $ substitution is to be expanded by dpkg-query:
+# shellcheck disable=SC2016
+binary_version="$(dpkg-query -W -f '${Version}' "$binary_package")"
+dpkg-repack --generate "$binary_package"
+assert grep -q '^Breaks:' dpkg-repack.*/DEBIAN/control
+assert_not grep -q '^Provides:' dpkg-repack.*/DEBIAN/control
+assert_not grep -q '^Replaces:' dpkg-repack.*/DEBIAN/control
+# The $ substitutions in the Perl expressions are to be expanded by Perl,
+# not by the shell, so:
+# shellcheck disable=SC2016
+env \
+    binary_package="$binary_package" \
+    binary_version="$binary_version" \
+    future_binary_package="$future_binary_package" \
+    perl -p -i \
+        -e 's/^Package:.*$/Package: $ENV{future_binary_package}/;' \
+        -e 's/^(Breaks:.*)$/$1, $ENV{binary_package}/;' \
+        dpkg-repack.*/DEBIAN/control
+echo "Replaces: ${binary_package}" | tee -a dpkg-repack.*/DEBIAN/control
+echo "Provides: ${binary_package} (= ${binary_version})" | tee -a dpkg-repack.*/DEBIAN/control
+dpkg-deb --build dpkg-repack.* "$future_binary_package.deb"
+dpkg-deb --info "$future_binary_package.deb"
+dpkg-deb --contents "$future_binary_package.deb"
+apt-get -y install ./"$future_binary_package.deb" dconf-gsettings-backend gsettings-desktop-schemas
+
+assert test -e /usr/share/glib-2.0/schemas/org.gnome.desktop.interface.gschema.xml
+assert test -s /usr/share/glib-2.0/schemas/gschemas.compiled
+
+for f in /usr/lib/*/gio/modules/libdconfsettings.so; do
+    assert test -e "$f"
+    assert test -s "$f"
+done
+
+for f in /usr/lib/*/gio/modules/giomodule.cache; do
+    assert test -e "$f"
+    assert test -s "$f"
+done
+
+# Purging the "old" (pre-transition) binary package does not destroy the
+# GIO modules and GSettings schema summaries
+apt-get -y purge "$binary_package"
+
+assert test -e /usr/share/glib-2.0/schemas/org.gnome.desktop.interface.gschema.xml
+assert test -s /usr/share/glib-2.0/schemas/gschemas.compiled
+
+for f in /usr/lib/*/gio/modules/libdconfsettings.so; do
+    assert test -e "$f"
+    assert test -s "$f"
+done
+
+for f in /usr/lib/*/gio/modules/giomodule.cache; do
+    assert test -e "$f"
+    assert test -s "$f"
+done
+
+# Purging the "new" (post-transition) binary package still *does* destroy the
+# GIO modules and GSettings schema summaries
+apt-get -y purge "$future_binary_package"
+
+assert_not test -e /usr/share/glib-2.0/schemas/org.gnome.desktop.interface.gschema.xml
+assert_not test -e /usr/share/glib-2.0/schemas/gschemas.compiled
+
+for f in /usr/lib/*/gio/modules/libdconfsettings.so; do
+    assert_not test -e "$f"
+done
+
+for f in /usr/lib/*/gio/modules/giomodule.cache; do
+    assert_not test -e "$f"
+done
+
+echo "1..$n" >&3
+exit "$failed"
+
+# vim:set sw=4 sts=4 et:
diff -Nru glib2.0-2.74.6/debian/tests/control glib2.0-2.74.6/debian/tests/control
--- glib2.0-2.74.6/debian/tests/control	2024-11-14 09:42:34.000000000 +0000
+++ glib2.0-2.74.6/debian/tests/control	2025-08-18 09:27:51.000000000 +0100
@@ -9,3 +9,7 @@
 Tests: closure-refcount debugcontroller gdbus-server-auth gdbus-threading gmenumodel mainloop memory-monitor-dbus socket testfilemonitor threadtests timeout timer
 Depends: dbus-daemon (>= 1.8), dbus-x11, gnome-desktop-testing, libglib2.0-tests, locales | locales-all, xauth, xvfb
 Restrictions: allow-stderr flaky
+
+Tests: 1065022-futureproofing
+Depends: dconf-gsettings-backend, dpkg-repack, gsettings-desktop-schemas, libglib2.0-0
+Restrictions: allow-stderr breaks-testbed flaky needs-root
diff -Nru glib2.0-2.74.6/glib/gfileutils.c glib2.0-2.74.6/glib/gfileutils.c
--- glib2.0-2.74.6/glib/gfileutils.c	2023-02-23 13:54:27.000000000 +0000
+++ glib2.0-2.74.6/glib/gfileutils.c	2025-08-18 10:52:22.000000000 +0100
@@ -1483,9 +1483,9 @@
   static const char letters[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
   static const int NLETTERS = sizeof (letters) - 1;
-  glong value;
-  gint64 now_us;
-  static int counter = 0;
+  guint64 value;
+  guint64 now_us;
+  static guint counter = 0;
 
   g_return_val_if_fail (tmpl != NULL, -1);
 
@@ -1504,7 +1504,7 @@
 
   for (count = 0; count < 100; value += 7777, ++count)
     {
-      glong v = value;
+      guint64 v = value;
 
       /* Fill in the random bits.  */
       XXXXXX[0] = letters[v % NLETTERS];
diff -Nru glib2.0-2.74.6/glib/gstring.c glib2.0-2.74.6/glib/gstring.c
--- glib2.0-2.74.6/glib/gstring.c	2023-02-23 13:54:27.000000000 +0000
+++ glib2.0-2.74.6/glib/gstring.c	2025-08-18 10:52:22.000000000 +0100
@@ -426,8 +426,9 @@
     return string;
 
   if (len < 0)
-    len = strlen (val);
-  len_unsigned = len;
+    len_unsigned = strlen (val);
+  else
+    len_unsigned = len;
 
   if (pos < 0)
     pos_unsigned = string->len;
@@ -725,10 +726,12 @@
   g_string_maybe_expand (string, 1);
 
   if (pos < 0)
-    pos = string->len;
+    pos_unsigned = string->len;
   else
-    g_return_val_if_fail ((gsize) pos <= string->len, string);
-  pos_unsigned = pos;
+    {
+      pos_unsigned = pos;
+      g_return_val_if_fail (pos_unsigned <= string->len, string);
+    }
 
   /* If not just an append, move the old stuff */
   if (pos_unsigned < string->len)
@@ -761,6 +764,7 @@
                          gssize    pos,
                          gunichar  wc)
 {
+  gsize pos_unsigned;
   gint charlen, first, i;
   gchar *dest;
 
@@ -802,15 +806,18 @@
   g_string_maybe_expand (string, charlen);
 
   if (pos < 0)
-    pos = string->len;
+    pos_unsigned = string->len;
   else
-    g_return_val_if_fail ((gsize) pos <= string->len, string);
+    {
+      pos_unsigned = pos;
+      g_return_val_if_fail (pos_unsigned <= string->len, string);
+    }
 
   /* If not just an append, move the old stuff */
-  if ((gsize) pos < string->len)
-    memmove (string->str + pos + charlen, string->str + pos, string->len - pos);
+  if (pos_unsigned < string->len)
+    memmove (string->str + pos_unsigned + charlen, string->str + pos_unsigned, string->len - pos_unsigned);
 
-  dest = string->str + pos;
+  dest = string->str + pos_unsigned;
   /* Code copied from g_unichar_to_utf() */
   for (i = charlen - 1; i > 0; --i)
     {
@@ -868,6 +875,7 @@
                         const gchar *val,
                         gssize       len)
 {
+  gsize len_unsigned;
   gsize end;
 
   g_return_val_if_fail (string != NULL, NULL);
@@ -879,14 +887,16 @@
   g_return_val_if_fail (pos <= string->len, string);
 
   if (len < 0)
-    len = strlen (val);
+    len_unsigned = strlen (val);
+  else
+    len_unsigned = len;
 
-  end = pos + len;
+  end = pos + len_unsigned;
 
   if (end > string->len)
     g_string_maybe_expand (string, end - string->len);
 
-  memcpy (string->str + pos, val, len);
+  memcpy (string->str + pos, val, len_unsigned);
 
   if (end > string->len)
     {

Reply to: