Bug#1104976: unblock (pre-approval): glib2.0/2.84.1-3
Package: release.debian.org
Severity: normal
Tags:
X-Debbugs-Cc: glib2.0@packages.debian.org, debian-boot@lists.debian.org
Control: affects -1 + src:glib2.0
User: release.debian.org@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 <smcv@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.astone@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 <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 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 <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 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 to: