Package: release.debian.org
Severity: normal
Tags: 
X-Debbugs-Cc: glib...@packages.debian.org, debian-b...@lists.debian.org
Control: affects -1 + src:glib2.0
User: release.debian....@packages.debian.org
Usertags: unblock

[ Reason ]
CVE-2025-4373 (#1104930).

I also took the opportunity to catch up with the upstream glib-2-84 
branch by adding one unrelated bugfix commit (a 1-line change).

[ Impact ]
Fixes an out-of-bounds write if an attacker can somehow arrange for GLib 
to be acting on overwhelmingly large strings (half the address space in 
a single GString object, so 2GB for 32-bit processes).

Ensures that localtime_r() is not called without first calling tzset(), 
which has unspecified behaviour.

[ Tests ]
Not yet tested. I will run autopkgtests and boot a GNOME system with the 
proposed GLib before upload, and inform this bug if further changes are 
needed.

GLib has a quite thorough test suite in general, but CVE-2025-4373 is 
not covered by it, because exploiting the bug requires a huge memory 
allocation that will, in practice, usually fail.

[ Risks ]
I can't think of any. If there is a problem, these changes would be easy 
to revert.

[ 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 testing
      (preliminary diff, will need a `dch -r` before release)

[ Other info ]
Needs a d-i ack due to the GTK-based graphical installer.

unblock glib2.0/2.84.1-3
diff --git a/debian/changelog b/debian/changelog
index 7f9c9d65fc..67651dcdd7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,28 @@
+glib2.0 (2.84.1-3) UNRELEASED; urgency=medium
+
+  [ Jeremy BĂ­cha ]
+  * d/p/gfileutils-Preserve-mode-during-atomic-updates.patch:
+    Add a note that this fix for LP#2072586 was reverted in the upstream
+    2.84.x branch as a behaviour change. It was kept in 2.85.x,
+    and seems reasonable to keep for trixie.
+
+  [ Simon McVittie ]
+  * d/p/gfileutils-Preserve-mode-during-atomic-updates.patch:
+    Add a cross-reference to LP#2072586
+  * 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 with very large
+    GString instances (Closes: #1104930, CVE-2025-4373)
+  * d/p/gdate-Call-tzset-before-localtime_r.patch:
+    Add patch from upstream to ensure that tzset() is called before
+    localtime_r(); otherwise the behaviour of localtime_r() is unspecified.
+  * These patches bring us up to date with upstream glib-2-84 branch commit
+    2.84.1-15-gb3de15acf9, excluding changes that are not relevant to
+    Debian architectures (macOS CI and Windows) and the revert of the fix
+    for LP#2072586 (discussed above).
+
+ -- Simon McVittie <s...@debian.org>  Fri, 09 May 2025 10:46:25 +0100
+
 glib2.0 (2.84.1-2) unstable; urgency=medium
 
   * Cherry-pick 2 commits from glib-2-84 branch (LP: #2072586)
diff --git a/debian/patches/gdate-Call-tzset-before-localtime_r.patch b/debian/patches/gdate-Call-tzset-before-localtime_r.patch
new file mode 100644
index 0000000000..05e378bfa6
--- /dev/null
+++ b/debian/patches/gdate-Call-tzset-before-localtime_r.patch
@@ -0,0 +1,24 @@
+From: Alessandro Astone <alessandro.ast...@canonical.com>
+Date: Wed, 30 Apr 2025 16:04:34 +0200
+Subject: gdate: Call tzset before localtime_r
+
+From `man 3 ctime`:
+  According to POSIX.1, localtime() is required to behave as though tzset(3)
+  was called, while localtime_r() does not have this requirement.
+  For portable code, tzset(3) should be called before localtime_r().
+---
+ glib/gdate.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/glib/gdate.c b/glib/gdate.c
+index ffc21f1..ed25ec3 100644
+--- a/glib/gdate.c
++++ b/glib/gdate.c
+@@ -1392,6 +1392,7 @@ _g_localtime (time_t timet, struct tm *out_tm)
+   gboolean success = TRUE;
+ 
+ #ifdef HAVE_LOCALTIME_R
++  tzset ();
+   if (!localtime_r (&timet, out_tm))
+     success = FALSE;
+ #else
diff --git a/debian/patches/gfileutils-Preserve-mode-during-atomic-updates.patch b/debian/patches/gfileutils-Preserve-mode-during-atomic-updates.patch
index 79e1c9c014..5d0771924c 100644
--- a/debian/patches/gfileutils-Preserve-mode-during-atomic-updates.patch
+++ b/debian/patches/gfileutils-Preserve-mode-during-atomic-updates.patch
@@ -13,6 +13,10 @@ Closes GNOME/dconf#76
 (cherry picked from commit 3cc0c0de33bc4b461e89b05d142e1ecf5f474317)
 
 Origin: upstream glib-2-84 branch, after 2.84.1
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/glib2.0/+bug/2072586
+Comment: This was actually reverted from the glib-2-84 branch but
+ it seems reasonable to keep it for trixie anyway
+ https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4608
 ---
  glib/gfileutils.c      | 25 +++++++++++++++++++++++--
  glib/tests/fileutils.c | 14 ++++++++++----
diff --git a/debian/patches/gstring-Make-len_unsigned-unsigned.patch b/debian/patches/gstring-Make-len_unsigned-unsigned.patch
new file mode 100644
index 0000000000..33b48bf796
--- /dev/null
+++ b/debian/patches/gstring-Make-len_unsigned-unsigned.patch
@@ -0,0 +1,25 @@
+From: Peter Bloomfield <peterbloomfi...@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 d79a484..2a399ee 100644
+--- a/glib/gstring.c
++++ b/glib/gstring.c
+@@ -928,7 +928,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 --git a/debian/patches/gstring-carefully-handle-gssize-parameters.patch b/debian/patches/gstring-carefully-handle-gssize-parameters.patch
new file mode 100644
index 0000000000..95c83f41f7
--- /dev/null
+++ b/debian/patches/gstring-carefully-handle-gssize-parameters.patch
@@ -0,0 +1,119 @@
+From: Michael Catanzaro <mcatanz...@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 <mcatanz...@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 5279ed3..d79a484 100644
+--- a/glib/gstring.c
++++ b/glib/gstring.c
+@@ -480,8 +480,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;
+@@ -778,10 +779,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)
+@@ -814,6 +817,7 @@ g_string_insert_unichar (GString  *string,
+                          gssize    pos,
+                          gunichar  wc)
+ {
++  gsize pos_unsigned;
+   gint charlen, first, i;
+   gchar *dest;
+ 
+@@ -855,15 +859,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)
+     {
+@@ -921,6 +928,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);
+@@ -932,14 +940,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 --git a/debian/patches/series b/debian/patches/series
index 797de6b376..fe3988c90c 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -16,3 +16,6 @@ workarounds/testfilemonitor-Skip-if-we-are-avoiding-flaky-tests.patch
 debian/girepository-Describe-the-Debian-specific-cross-prefixed-.patch
 gclosure-fix-ATOMIC_CHANGE_FIELD-to-read-vint-atomically.patch
 gfileutils-Preserve-mode-during-atomic-updates.patch
+gstring-carefully-handle-gssize-parameters.patch
+gstring-Make-len_unsigned-unsigned.patch
+gdate-Call-tzset-before-localtime_r.patch
diff --git a/glib/gdate.c b/glib/gdate.c
index ffc21f1b9b..ed25ec308c 100644
--- a/glib/gdate.c
+++ b/glib/gdate.c
@@ -1392,6 +1392,7 @@ _g_localtime (time_t timet, struct tm *out_tm)
   gboolean success = TRUE;
 
 #ifdef HAVE_LOCALTIME_R
+  tzset ();
   if (!localtime_r (&timet, out_tm))
     success = FALSE;
 #else
diff --git a/glib/gstring.c b/glib/gstring.c
index 5279ed3cca..2a399ee21f 100644
--- a/glib/gstring.c
+++ b/glib/gstring.c
@@ -480,8 +480,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;
@@ -778,10 +779,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)
@@ -814,6 +817,7 @@ g_string_insert_unichar (GString  *string,
                          gssize    pos,
                          gunichar  wc)
 {
+  gsize pos_unsigned;
   gint charlen, first, i;
   gchar *dest;
 
@@ -855,15 +859,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)
     {
@@ -921,6 +928,7 @@ g_string_overwrite_len (GString     *string,
                         const gchar *val,
                         gssize       len)
 {
+  gsize len_unsigned;
   gsize end;
 
   g_return_val_if_fail (string != NULL, NULL);
@@ -932,14 +940,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)
     {

Reply via email to