gprintf: Fix invalid size allocation in g_vasprintf()

As per the previous commit, it’s possible for
`g_printf_string_upper_bound()` to return an error. We need to catch and
handle that error in `g_vasprintf()` to avoid it trying to write to a
`NULL` string allocation and crashing.

So, call `g_vsnprintf()` directly instead of calling
`g_printf_string_upper_bound()`, so that the error case can be handled.

There was already a test for some of this behaviour
(`test_vasprintf_invalid_format_placeholder()`). Because it tested an
invalid format string, the `_g_vsprintf()` call bailed out before
checking whether the buffer it had been passed was `NULL`. The new test
has a valid format string, but an invalid arg.

When running the tests locally, I have disabled the `USE_SYSTEM_PRINTF`
and `HAVE_VASPRINTF` code paths in `g_vasprintf()`.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>

Fixes: #3187
This commit is contained in:
Philip Withnall 2023-11-27 11:08:00 +00:00
parent e2cd0962c2
commit 3c6052f318
2 changed files with 41 additions and 6 deletions

View File

@ -359,19 +359,29 @@ g_vasprintf (gchar **string,
{
va_list args2;
char c;
int max_len;
va_copy (args2, args);
*string = g_new (gchar, g_printf_string_upper_bound (format, args));
max_len = _g_vsnprintf (&c, 1, format, args);
if (max_len < 0)
{
/* This can happen if @format contains `%ls` or `%lc` and @args contains
* something not representable in the current locales encoding (which
* should be UTF-8, but ymmv). Basically: dont use `%ls` or `%lc`. */
va_end (args2);
*string = NULL;
return -1;
}
*string = g_new (gchar, (size_t) max_len + 1);
len = _g_vsprintf (*string, format, args2);
va_end (args2);
if (len < 0)
{
g_free (*string);
*string = NULL;
}
/* _g_vsprintf() should have exactly the same failure modes as _g_vsnprintf() */
g_assert (len >= 0);
}
#endif

View File

@ -948,6 +948,30 @@ test_vasprintf_invalid_format_placeholder (void)
#endif
}
static void
test_vasprintf_invalid_wide_string (void)
{
#if !defined(__APPLE__) && !defined(__FreeBSD__)
gint len = 0;
gchar *buf = "some non-null string";
#endif
g_test_summary ("Test error handling for invalid wide strings in g_vasprintf()");
#if !defined(__APPLE__) && !defined(__FreeBSD__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
#pragma GCC diagnostic ignored "-Wformat-extra-args"
len = test_vasprintf_va (&buf, "%ls", L"\xD800" /* incomplete surrogate pair */);
#pragma GCC diagnostic pop
g_assert_cmpint (len, ==, -1);
g_assert_null (buf);
#else
g_test_skip ("vasprintf() placeholder checks on BSDs are less strict");
#endif
}
int
main (int argc,
char *argv[])
@ -989,6 +1013,7 @@ main (int argc,
g_test_add_func ("/sprintf/upper-bound", test_upper_bound);
g_test_add_func ("/vasprintf/invalid-format-placeholder", test_vasprintf_invalid_format_placeholder);
g_test_add_func ("/vasprintf/invalid-wide-string", test_vasprintf_invalid_wide_string);
return g_test_run();
}