diff --git a/glib/gprintf.c b/glib/gprintf.c index a0ccef9ab..19642d82b 100644 --- a/glib/gprintf.c +++ b/glib/gprintf.c @@ -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 locale’s encoding (which + * should be UTF-8, but ymmv). Basically: don’t 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 diff --git a/glib/tests/test-printf.c b/glib/tests/test-printf.c index faec8e110..432685e3f 100644 --- a/glib/tests/test-printf.c +++ b/glib/tests/test-printf.c @@ -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(); }