gvariant: Optimise g_variant_print() for nested maybes

As with commit 9ae59bd647, deeply nested
maybes in an array can be exploited by a malicious caller to cause a
geometric increase in processing time and number of `GVariant` instances
handled by the `g_variant_print()` code.

Optimise this by skipping recursing through most of the chain of maybes,
thus avoiding all the setup checks in each recursive call.

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

oss-fuzz#54577
This commit is contained in:
Philip Withnall 2022-12-29 18:42:44 +00:00
parent b65e50cff5
commit a1213e343e

View File

@ -2226,8 +2226,9 @@ g_variant_print_string (GVariant *value,
if (g_variant_n_children (value))
{
gchar *printed_child;
GVariant *element;
const GVariantType *base_type;
guint i, depth;
GVariant *element = NULL;
/* Nested maybes:
*
@ -2241,19 +2242,36 @@ g_variant_print_string (GVariant *value,
* "just" is actually exactly the case where we have a nested
* Nothing.
*
* Instead of searching for that nested Nothing, we just print
* the contained value into a separate string and see if we
* end up with "nothing" at the end of it. If so, we need to
* add "just" at our level.
* Search for the nested Nothing, to save a lot of recursion if there
* are multiple levels of maybes.
*/
element = g_variant_get_child_value (value, 0);
printed_child = g_variant_print (element, FALSE);
g_variant_unref (element);
for (depth = 0, base_type = g_variant_get_type (value);
g_variant_type_is_maybe (base_type);
depth++, base_type = g_variant_type_element (base_type));
if (g_str_has_suffix (printed_child, "nothing"))
g_string_append (string, "just ");
g_string_append (string, printed_child);
g_free (printed_child);
element = g_variant_ref (value);
for (i = 0; i < depth && element != NULL; i++)
{
GVariant *new_element = g_variant_n_children (element) ? g_variant_get_child_value (element, 0) : NULL;
g_variant_unref (element);
element = g_steal_pointer (&new_element);
}
if (element == NULL)
{
/* One of the maybes was Nothing, so print out the right number of
* justs. */
for (; i > 1; i--)
g_string_append (string, "just ");
g_string_append (string, "nothing");
}
else
{
/* There are no Nothings, so print out the child with no prefixes. */
g_variant_print_string (element, string, FALSE);
}
g_clear_pointer (&element, g_variant_unref);
}
else
g_string_append (string, "nothing");