diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c index efad35619..1a6697797 100644 --- a/glib/gvariant-parser.c +++ b/glib/gvariant-parser.c @@ -401,6 +401,8 @@ token_stream_end_ref (TokenStream *stream, ref->end = stream->stream - stream->start; } +/* This is guaranteed to write exactly as many bytes to `out` as it consumes + * from `in`. i.e. The `out` buffer doesn’t need to be any longer than `in`. */ static void pattern_copy (gchar **out, const gchar **in) @@ -431,15 +433,22 @@ pattern_coalesce (const gchar *left, { gchar *result; gchar *out; + size_t buflen; + size_t left_len = strlen (left), right_len = strlen (right); /* the length of the output is loosely bound by the sum of the input * lengths, not simply the greater of the two lengths. * - * (*(iii)) + ((iii)*) ((iii)(iii)) + * (*(iii)) + ((iii)*) = ((iii)(iii)) * - * 8 + 8 = 12 + * 8 + 8 = 12 + * + * This can be proven by the fact that `out` is never incremented by more + * bytes than are consumed from `left` or `right` in each iteration. */ - out = result = g_malloc (strlen (left) + strlen (right)); + g_assert (left_len < G_MAXSIZE - right_len); + buflen = left_len + right_len + 1; + out = result = g_malloc (buflen); while (*left && *right) { @@ -493,6 +502,9 @@ pattern_coalesce (const gchar *left, } } + /* Need at least one byte remaining for trailing nul. */ + g_assert (out < result + buflen); + if (*left || *right) { g_free (result); diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c index 27586be71..2aa80d6da 100644 --- a/glib/tests/gvariant.c +++ b/glib/tests/gvariant.c @@ -4014,7 +4014,7 @@ test_parses (void) g_free (printed); } - /* pattern coalese of `MN` and `*` is `MN` */ + /* pattern coalesce of `MN` and `*` is `MN` */ { GVariant *value = NULL; GError *error = NULL; @@ -4025,6 +4025,29 @@ test_parses (void) g_variant_unref (value); } + /* pattern coalesce of `u` and `u` is `u`; this operates close to the string + * length bounds in pattern_coalesce() */ + { + GVariant *value = NULL; + GError *error = NULL; + + value = g_variant_parse (NULL, "[@u 5, @u 15]", NULL, NULL, &error); + g_assert_no_error (error); + g_assert_cmpstr (g_variant_get_type_string (value), ==, "au"); + g_variant_unref (value); + } + + /* pattern coalesce of `(Ma*Ma(iii))` and `(Ma(iii)Ma*)` is `(Ma(iii)Ma(iii))` */ + { + GVariant *value = NULL; + GError *error = NULL; + + value = g_variant_parse (NULL, "[([], [(1,2,3)]), ([(1,2,3)], [])]", NULL, NULL, &error); + g_assert_no_error (error); + g_assert_cmpstr (g_variant_get_type_string (value), ==, "a(a(iii)a(iii))"); + g_variant_unref (value); + } + #ifndef _MSC_VER /* inf/nan strings are C99 features which Visual C++ does not support */ /* inf/nan mini test */