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.
This commit is contained in:
Tobias Stoeckmann
2025-07-07 20:52:24 +02:00
parent d0f31c23d5
commit 6aa97beda3
2 changed files with 23 additions and 5 deletions
+5 -5
View File
@@ -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);
}
+18
View File
@@ -801,6 +801,23 @@ test_string_copy (void)
g_string_free (string2, 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[])
@@ -831,6 +848,7 @@ main (int argc,
g_test_add_func ("/string/new-take", test_string_new_take);
g_test_add_func ("/string/new-take/null", test_string_new_take_null);
g_test_add_func ("/string/copy", test_string_copy);
g_test_add_func ("/string/sized-new", test_string_sized_new);
return g_test_run();
}