From 6aa97beda32bb337370858862f4efe2f3372619f Mon Sep 17 00:00:00 2001
From: Tobias Stoeckmann <tobias@stoeckmann.org>
Date: Mon, 7 Jul 2025 20:52:24 +0200
Subject: [PATCH] gstring: Fix g_string_sized_new segmentation fault

If glib is compiled with -Dglib_assert=false, i.e. no asserts
enabled, then g_string_sized_new(G_MAXSIZE) leads to a segmentation
fault due to an out of boundary write.

This happens because the overflow check was moved into
g_string_maybe_expand which is not called by g_string_sized_new.

By assuming that string->allocated_len is always larger than
string->len (and the code would be in huge trouble if that is not true),
the G_UNLIKELY check in g_string_maybe_expand can be rephrased to
avoid a potential G_MAXSIZE overflow.

This in turn leads to 150-200 bytes smaller compiled library
depending on gcc and clang versions, and one less check for the most
common code paths.

Reverts https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4655 and
reorders internal g_string_maybe_expand check to still fix
CVE-2025-6052.

CVE: CVE-2025-6052
Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/glib/-/commit/6aa97beda32bb337370858862f4efe2f3372619f]
Signed-off-by: Peter Marko <peter.marko@siemens.com>
---
 glib/gstring.c      | 10 +++++-----
 glib/tests/string.c | 18 ++++++++++++++++++
 2 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/glib/gstring.c b/glib/gstring.c
index 010a8e976..24c4bfb40 100644
--- a/glib/gstring.c
+++ b/glib/gstring.c
@@ -68,6 +68,10 @@ static void
 g_string_expand (GString *string,
                  gsize    len)
 {
+  /* Detect potential overflow */
+  if G_UNLIKELY ((G_MAXSIZE - string->len - 1) < len)
+    g_error ("adding %" G_GSIZE_FORMAT " to string would overflow", len);
+
   string->allocated_len = g_nearest_pow (string->len + len + 1);
   /* If the new size is bigger than G_MAXSIZE / 2, only allocate enough
    * memory for this string and don't over-allocate.
@@ -82,11 +86,7 @@ static inline void
 g_string_maybe_expand (GString *string,
                        gsize    len)
 {
-  /* Detect potential overflow */
-  if G_UNLIKELY ((G_MAXSIZE - string->len - 1) < len)
-    g_error ("adding %" G_GSIZE_FORMAT " to string would overflow", len);
-
-  if (G_UNLIKELY (string->len + len >= string->allocated_len))
+  if (G_UNLIKELY (len >= string->allocated_len - string->len))
     g_string_expand (string, len);
 }
 
diff --git a/glib/tests/string.c b/glib/tests/string.c
index aa363c57a..e3bc4a02e 100644
--- a/glib/tests/string.c
+++ b/glib/tests/string.c
@@ -767,6 +767,23 @@ test_string_new_take_null (void)
   g_string_free (g_steal_pointer (&string), TRUE);
 }
 
+static void
+test_string_sized_new (void)
+{
+
+  if (g_test_subprocess ())
+    {
+      GString *string = g_string_sized_new (G_MAXSIZE);
+      g_string_free (string, TRUE);
+    }
+  else
+    {
+      g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
+      g_test_trap_assert_failed ();
+      g_test_trap_assert_stderr ("*string would overflow*");
+    }
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -796,6 +813,7 @@ main (int   argc,
   g_test_add_func ("/string/test-string-steal", test_string_steal);
   g_test_add_func ("/string/test-string-new-take", test_string_new_take);
   g_test_add_func ("/string/test-string-new-take/null", test_string_new_take_null);
+  g_test_add_func ("/string/sized-new", test_string_sized_new);
 
   return g_test_run();
 }
