gvarianttype: Impose a recursion limit of 128 on variant types

Previously, GVariant has allowed ‘arbitrary’ recursion on GVariantTypes,
but this isn’t really feasible. We have to deal with GVariants from
untrusted sources, and the nature of GVariantType means that another
level of recursion (and hence, for example, another stack frame in your
application) can be added with a single byte in a variant type signature
in the input. This gives malicious input sources far too much leverage
to cause deep stack recursion or massive memory allocations which can
DoS an application.

Limit recursion to 128 levels (which should be more than enough for
anyone™), document it and add a test. This is, handily, also the limit
of 64 applied by the D-Bus specification (§(Valid Signatures)), plus a
bit to allow wrapping of D-Bus messages in additional layers of
variants.

oss-fuzz#9857

Signed-off-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
Philip Withnall 2018-09-07 20:27:39 +01:00
parent eb7c9adc3b
commit 7c4e6e9fbe
10 changed files with 398 additions and 45 deletions

View File

@ -3431,6 +3431,7 @@ g_variant_parse_error_print_context
g_variant_parse_error_quark
g_variant_parser_get_error_quark
g_variant_type_checked_
g_variant_type_string_get_depth_
</SECTION>

View File

@ -20,6 +20,7 @@
#include <glib/gvariant-core.h>
#include <glib/gvariant-internal.h>
#include <glib/gvariant-serialiser.h>
#include <glib/gtestutils.h>
#include <glib/gbitlock.h>
@ -74,6 +75,7 @@ struct _GVariant
gint state;
gint ref_count;
gsize depth;
};
/* struct GVariant:
@ -202,6 +204,11 @@ struct _GVariant
* reference. See g_variant_ref_sink().
*
* ref_count: the reference count of the instance
*
* depth: the depth of the GVariant in a hierarchy of nested containers,
* increasing with the level of nesting. The top-most GVariant has depth
* zero. This is used to avoid recursing too deeply and overflowing the
* stack when handling deeply nested untrusted serialised GVariants.
*/
#define STATE_LOCKED 1
#define STATE_SERIALISED 2
@ -366,6 +373,7 @@ g_variant_serialise (GVariant *value,
serialised.type_info = value->type_info;
serialised.size = value->size;
serialised.data = data;
serialised.depth = value->depth;
children = (gpointer *) value->contents.tree.children;
n_children = value->contents.tree.n_children;
@ -407,6 +415,7 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
if (serialised->size == 0)
serialised->size = value->size;
g_assert (serialised->size == value->size);
serialised->depth = value->depth;
if (serialised->data)
/* g_variant_store() is a public API, so it
@ -480,6 +489,7 @@ g_variant_alloc (const GVariantType *type,
STATE_FLOATING;
value->size = (gssize) -1;
value->ref_count = 1;
value->depth = 0;
return value;
}
@ -933,7 +943,8 @@ g_variant_n_children (GVariant *value)
GVariantSerialised serialised = {
value->type_info,
(gpointer) value->contents.serialised.data,
value->size
value->size,
value->depth,
};
n_children = g_variant_serialised_n_children (serialised);
@ -962,6 +973,11 @@ g_variant_n_children (GVariant *value)
* The returned value is never floating. You should free it with
* g_variant_unref() when you're done with it.
*
* There may be implementation specific restrictions on deeply nested values,
* which would result in the unit tuple being returned as the child value,
* instead of further nested children. #GVariant is guaranteed to handle
* nesting up to at least 64 levels.
*
* This function is O(1).
*
* Returns: (transfer full): the child at the specified index
@ -973,6 +989,7 @@ g_variant_get_child_value (GVariant *value,
gsize index_)
{
g_return_val_if_fail (index_ < g_variant_n_children (value), NULL);
g_return_val_if_fail (value->depth < G_MAXSIZE, NULL);
if (~g_atomic_int_get (&value->state) & STATE_SERIALISED)
{
@ -995,7 +1012,8 @@ g_variant_get_child_value (GVariant *value,
GVariantSerialised serialised = {
value->type_info,
(gpointer) value->contents.serialised.data,
value->size
value->size,
value->depth,
};
GVariantSerialised s_child;
GVariant *child;
@ -1005,6 +1023,20 @@ g_variant_get_child_value (GVariant *value,
*/
s_child = g_variant_serialised_get_child (serialised, index_);
/* Check whether this would cause nesting too deep. If so, return a fake
* child. The only situation we expect this to happen in is with a variant,
* as all other deeply-nested types have a static type, and hence should
* have been rejected earlier. In the case of a variant whose nesting plus
* the depth of its child is too great, return a unit variant () instead of
* the real child. */
if (!(value->state & STATE_TRUSTED) &&
g_variant_type_info_query_depth (s_child.type_info) >=
G_VARIANT_MAX_RECURSION_DEPTH - value->depth)
{
g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT));
return g_variant_new_tuple (NULL, 0);
}
/* create a new serialised instance out of it */
child = g_slice_new (GVariant);
child->type_info = s_child.type_info;
@ -1012,6 +1044,7 @@ g_variant_get_child_value (GVariant *value,
STATE_SERIALISED;
child->size = s_child.size;
child->ref_count = 1;
child->depth = value->depth + 1;
child->contents.serialised.bytes =
g_bytes_ref (value->contents.serialised.bytes);
child->contents.serialised.data = s_child.data;
@ -1074,6 +1107,9 @@ g_variant_store (GVariant *value,
* being trusted. If the value was already marked as being trusted then
* this function will immediately return %TRUE.
*
* There may be implementation specific restrictions on deeply nested values.
* GVariant is guaranteed to handle nesting up to at least 64 levels.
*
* Returns: %TRUE if @value is in normal form
*
* Since: 2.24
@ -1086,12 +1122,16 @@ g_variant_is_normal_form (GVariant *value)
g_variant_lock (value);
if (value->depth >= G_VARIANT_MAX_RECURSION_DEPTH)
return FALSE;
if (value->state & STATE_SERIALISED)
{
GVariantSerialised serialised = {
value->type_info,
(gpointer) value->contents.serialised.data,
value->size
value->size,
value->depth
};
if (g_variant_serialised_is_normal (serialised))

View File

@ -47,4 +47,22 @@ GVariantType * g_variant_format_string_scan_type (const g
const gchar *limit,
const gchar **endptr);
/* The maximum number of levels of nested container which this implementation
* of #GVariant will handle.
*
* The limit must be at least 64 + 1, to allow D-Bus messages to be wrapped in
* a top-level #GVariant. This comes from the D-Bus specification (§(Valid
* Signatures)), but also seems generally reasonable. #GDBusMessage wraps its
* payload in a top-level tuple.
*
* The limit is actually set to be a lot greater than 64, to allow much greater
* nesting of values off D-Bus. It cannot be set over around 80000, or the risk
* of overflowing the stack when parsing type strings becomes too great.
*
* Aside from those constraints, the choice of this value is arbitrary. The
* only restrictions on it from the API are that it has to be greater than 64
* (due to D-Bus).
*/
#define G_VARIANT_MAX_RECURSION_DEPTH ((gsize) 128)
#endif /* __G_VARIANT_INTERNAL_H__ */

View File

@ -23,6 +23,7 @@
#include "gvariant-serialiser.h"
#include <glib/gvariant-internal.h>
#include <glib/gtestutils.h>
#include <glib/gstrfuncs.h>
#include <glib/gtypes.h>
@ -81,7 +82,9 @@
* values is permitted (eg: 0 to 255 is a valid byte). Special checks
* need to be performed for booleans (only 0 or 1 allowed), strings
* (properly nul-terminated) and object paths and signature strings
* (meeting the D-Bus specification requirements).
* (meeting the D-Bus specification requirements). Depth checks need to be
* performed for nested types (arrays, tuples, and variants), to avoid massive
* recursion which could exhaust our stack when handling untrusted input.
*/
/* < private >
@ -113,6 +116,9 @@
* fixed-sized type, yet @size must be non-zero. The effect of this
* combination should be as if @data were a pointer to an
* appropriately-sized zero-filled region.
*
* @depth has no restrictions; the depth of a top-level serialised #GVariant is
* zero, and it increases for each level of nested child.
*/
/* < private >
@ -255,6 +261,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
*/
value.type_info = g_variant_type_info_element (value.type_info);
g_variant_type_info_ref (value.type_info);
value.depth++;
return value;
}
@ -286,7 +293,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
{
if (n_children)
{
GVariantSerialised child = { NULL, value.data, value.size };
GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1 };
gvs_filler (&child, children[0]);
}
@ -307,6 +314,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
/* proper element size: "Just". recurse to the child. */
value.type_info = g_variant_type_info_element (value.type_info);
value.depth++;
return g_variant_serialised_is_normal (value);
}
@ -347,6 +355,8 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
if (value.size == 0)
value.data = NULL;
value.depth++;
return value;
}
@ -376,7 +386,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value,
{
if (n_children)
{
GVariantSerialised child = { NULL, value.data, value.size - 1 };
GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1 };
/* write the data for the child. */
gvs_filler (&child, children[0]);
@ -395,6 +405,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
value.type_info = g_variant_type_info_element (value.type_info);
value.size--;
value.depth++;
return g_variant_serialised_is_normal (value);
}
@ -445,6 +456,7 @@ gvs_fixed_sized_array_get_child (GVariantSerialised value,
g_variant_type_info_query (child.type_info, NULL, &child.size);
child.data = value.data + (child.size * index_);
g_variant_type_info_ref (child.type_info);
child.depth = value.depth + 1;
return child;
}
@ -474,6 +486,7 @@ gvs_fixed_sized_array_serialise (GVariantSerialised value,
child.type_info = g_variant_type_info_element (value.type_info);
g_variant_type_info_query (child.type_info, NULL, &child.size);
child.data = value.data;
child.depth = value.depth + 1;
for (i = 0; i < n_children; i++)
{
@ -489,6 +502,7 @@ gvs_fixed_sized_array_is_normal (GVariantSerialised value)
child.type_info = g_variant_type_info_element (value.type_info);
g_variant_type_info_query (child.type_info, NULL, &child.size);
child.depth = value.depth + 1;
if (value.size % child.size != 0)
return FALSE;
@ -655,6 +669,7 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
child.type_info = g_variant_type_info_element (value.type_info);
g_variant_type_info_ref (child.type_info);
child.depth = value.depth + 1;
offset_size = gvs_get_offset_size (value.size);
@ -783,6 +798,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
child.type_info = g_variant_type_info_element (value.type_info);
g_variant_type_info_query (child.type_info, &alignment, NULL);
child.depth = value.depth + 1;
offset = 0;
for (i = 0; i < length; i++)
@ -858,6 +874,7 @@ gvs_tuple_get_child (GVariantSerialised value,
member_info = g_variant_type_info_member_info (value.type_info, index_);
child.type_info = g_variant_type_info_ref (member_info->type_info);
child.depth = value.depth + 1;
offset_size = gvs_get_offset_size (value.size);
/* tuples are the only (potentially) fixed-sized containers, so the
@ -1042,6 +1059,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
member_info = g_variant_type_info_member_info (value.type_info, i);
child.type_info = member_info->type_info;
child.depth = value.depth + 1;
g_variant_type_info_query (child.type_info, &alignment, &fixed_size);
@ -1171,8 +1189,10 @@ gvs_variant_get_child (GVariantSerialised value,
if (g_variant_type_is_definite (type))
{
gsize fixed_size;
gsize child_type_depth;
child.type_info = g_variant_type_info_get (type);
child.depth = value.depth + 1;
if (child.size != 0)
/* only set to non-%NULL if size > 0 */
@ -1180,8 +1200,10 @@ gvs_variant_get_child (GVariantSerialised value,
g_variant_type_info_query (child.type_info,
NULL, &fixed_size);
child_type_depth = g_variant_type_info_query_depth (child.type_info);
if (!fixed_size || fixed_size == child.size)
if ((!fixed_size || fixed_size == child.size) &&
value.depth < G_VARIANT_MAX_RECURSION_DEPTH - child_type_depth)
return child;
g_variant_type_info_unref (child.type_info);
@ -1193,6 +1215,7 @@ gvs_variant_get_child (GVariantSerialised value,
child.type_info = g_variant_type_info_get (G_VARIANT_TYPE_UNIT);
child.data = NULL;
child.size = 1;
child.depth = value.depth + 1;
return child;
}
@ -1234,10 +1257,13 @@ gvs_variant_is_normal (GVariantSerialised value)
{
GVariantSerialised child;
gboolean normal;
gsize child_type_depth;
child = gvs_variant_get_child (value, 0);
child_type_depth = g_variant_type_info_query_depth (child.type_info);
normal = (child.data != NULL || child.size == 0) &&
normal = (value.depth < G_VARIANT_MAX_RECURSION_DEPTH - child_type_depth) &&
(child.data != NULL || child.size == 0) &&
g_variant_serialised_is_normal (child);
g_variant_type_info_unref (child.type_info);
@ -1555,6 +1581,8 @@ g_variant_serialised_is_normal (GVariantSerialised serialised)
if (serialised.data == NULL)
return FALSE;
if (serialised.depth >= G_VARIANT_MAX_RECURSION_DEPTH)
return FALSE;
/* some hard-coded terminal cases */
switch (g_variant_type_info_get_type_char (serialised.type_info))

View File

@ -28,6 +28,7 @@ typedef struct
GVariantTypeInfo *type_info;
guchar *data;
gsize size;
gsize depth; /* same semantics as GVariant.depth */
} GVariantSerialised;
/* deserialisation */

View File

@ -24,6 +24,7 @@
#include <glib/gtestutils.h>
#include <glib/gstrfuncs.h>
#include <glib/gvariant-internal.h>
#include <string.h>
@ -114,7 +115,10 @@
*
* The above definition is recursive to arbitrary depth. "aaaaai" and
* "(ui(nq((y)))s)" are both valid type strings, as is
* "a(aa(ui)(qna{ya(yd)}))".
* "a(aa(ui)(qna{ya(yd)}))". In order to not hit memory limits, #GVariant
* imposes a limit on recursion depth of 65 nested containers. This is the
* limit in the D-Bus specification (64) plus one to allow a #GDBusMessage to
* be nested in a top-level tuple.
*
* The meaning of each of the characters is as follows:
* - `b`: the type string of %G_VARIANT_TYPE_BOOLEAN; a boolean value.
@ -194,6 +198,76 @@ g_variant_type_check (const GVariantType *type)
#endif
}
static gboolean
variant_type_string_scan_internal (const gchar *string,
const gchar *limit,
const gchar **endptr,
gsize *depth,
gsize depth_limit)
{
gsize max_depth = 0, child_depth;
g_return_val_if_fail (string != NULL, FALSE);
if (string == limit || *string == '\0')
return FALSE;
switch (*string++)
{
case '(':
while (string == limit || *string != ')')
{
if (depth_limit == 0 ||
!variant_type_string_scan_internal (string, limit, &string,
&child_depth,
depth_limit - 1))
return FALSE;
max_depth = MAX (max_depth, child_depth + 1);
}
string++;
break;
case '{':
if (depth_limit == 0 ||
string == limit || *string == '\0' || /* { */
!strchr ("bynqihuxtdsog?", *string++) || /* key */
!variant_type_string_scan_internal (string, limit, &string,
&child_depth, depth_limit - 1) || /* value */
string == limit || *string++ != '}') /* } */
return FALSE;
max_depth = MAX (max_depth, child_depth + 1);
break;
case 'm': case 'a':
if (depth_limit == 0 ||
!variant_type_string_scan_internal (string, limit, &string,
&child_depth, depth_limit - 1))
return FALSE;
max_depth = MAX (max_depth, child_depth + 1);
break;
case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
case 'x': case 't': case 'd': case 's': case 'o': case 'g':
case 'v': case 'r': case '*': case '?': case 'h':
max_depth = MAX (max_depth, 1);
break;
default:
return FALSE;
}
if (endptr != NULL)
*endptr = string;
if (depth != NULL)
*depth = max_depth;
return TRUE;
}
/**
* g_variant_type_string_scan:
* @string: a pointer to any string
@ -223,46 +297,37 @@ g_variant_type_string_scan (const gchar *string,
const gchar *limit,
const gchar **endptr)
{
g_return_val_if_fail (string != NULL, FALSE);
return variant_type_string_scan_internal (string, limit, endptr, NULL,
G_VARIANT_MAX_RECURSION_DEPTH);
}
if (string == limit || *string == '\0')
return FALSE;
/* < private >
* g_variant_type_string_get_depth_:
* @type_string: a pointer to any string
*
* Get the maximum depth of the nested types in @type_string. A basic type will
* return depth 1, and a container type will return a greater value. The depth
* of a tuple is 1 plus the depth of its deepest child type.
*
* If @type_string is not a valid #GVariant type string, 0 will be returned.
*
* Returns: depth of @type_string, or 0 on error
* Since: 2.60
*/
gsize
g_variant_type_string_get_depth_ (const gchar *type_string)
{
const gchar *endptr;
gsize depth = 0;
switch (*string++)
{
case '(':
while (string == limit || *string != ')')
if (!g_variant_type_string_scan (string, limit, &string))
return FALSE;
g_return_val_if_fail (type_string != NULL, 0);
string++;
break;
if (!variant_type_string_scan_internal (type_string, NULL, &endptr, &depth,
G_VARIANT_MAX_RECURSION_DEPTH) ||
*endptr != '\0')
return 0;
case '{':
if (string == limit || *string == '\0' || /* { */
!strchr ("bynqihuxtdsog?", *string++) || /* key */
!g_variant_type_string_scan (string, limit, &string) || /* value */
string == limit || *string++ != '}') /* } */
return FALSE;
break;
case 'm': case 'a':
return g_variant_type_string_scan (string, limit, endptr);
case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
case 'x': case 't': case 'd': case 's': case 'o': case 'g':
case 'v': case 'r': case '*': case '?': case 'h':
break;
default:
return FALSE;
}
if (endptr != NULL)
*endptr = string;
return TRUE;
return depth;
}
/**

View File

@ -374,6 +374,8 @@ GVariantType * g_variant_type_new_dict_entry (const G
/*< private >*/
GLIB_AVAILABLE_IN_ALL
const GVariantType * g_variant_type_checked_ (const gchar *);
GLIB_AVAILABLE_IN_2_60
gsize g_variant_type_string_get_depth_ (const gchar *type_string);
G_END_DECLS

View File

@ -251,6 +251,31 @@ g_variant_type_info_query (GVariantTypeInfo *info,
*fixed_size = info->fixed_size;
}
/* < private >
* g_variant_type_info_query_depth:
* @info: a #GVariantTypeInfo
*
* Queries @info to determine the depth of the type.
*
* See g_variant_type_string_get_depth_() for more details.
*
* Returns: depth of @info
* Since: 2.60
*/
gsize
g_variant_type_info_query_depth (GVariantTypeInfo *info)
{
g_variant_type_info_check (info, 0);
if (info->container_class)
{
ContainerInfo *container = (ContainerInfo *) info;
return g_variant_type_string_get_depth_ (container->type_string);
}
return 1;
}
/* == array == */
#define GV_ARRAY_INFO_CLASS 'a'
static ArrayInfo *

View File

@ -130,6 +130,8 @@ GLIB_AVAILABLE_IN_ALL
void g_variant_type_info_query (GVariantTypeInfo *typeinfo,
guint *alignment,
gsize *size);
GLIB_AVAILABLE_IN_2_60
gsize g_variant_type_info_query_depth (GVariantTypeInfo *typeinfo);
/* array */
GLIB_AVAILABLE_IN_ALL

View File

@ -661,6 +661,57 @@ test_gvarianttype (void)
}
}
/* Test that scanning a deeply recursive type string doesnt exhaust our
* stack space (which it would if the type string scanner was recursive). */
static void
test_gvarianttype_string_scan_recursion_tuple (void)
{
gchar *type_string = NULL;
gsize type_string_len = 1000001; /* not including nul terminator */
gsize i;
/* Build a long type string of ((…u…)). */
type_string = g_new0 (gchar, type_string_len + 1);
for (i = 0; i < type_string_len; i++)
{
if (i < type_string_len / 2)
type_string[i] = '(';
else if (i == type_string_len / 2)
type_string[i] = 'u';
else
type_string[i] = ')';
}
/* Goes (way) over allowed recursion limit. */
g_assert_false (g_variant_type_string_is_valid (type_string));
g_free (type_string);
}
/* Same as above, except with an array rather than a tuple. */
static void
test_gvarianttype_string_scan_recursion_array (void)
{
gchar *type_string = NULL;
gsize type_string_len = 1000001; /* not including nul terminator */
gsize i;
/* Build a long type string of aaa…aau. */
type_string = g_new0 (gchar, type_string_len + 1);
for (i = 0; i < type_string_len; i++)
{
if (i < type_string_len - 1)
type_string[i] = 'a';
else
type_string[i] = 'u';
}
/* Goes (way) over allowed recursion limit. */
g_assert_false (g_variant_type_string_is_valid (type_string));
g_free (type_string);
}
#define ALIGNED(x, y) (((x + (y - 1)) / y) * y)
/* do our own calculation of the fixed_size and alignment of a type
@ -1230,6 +1281,8 @@ random_instance_filler (GVariantSerialised *serialised,
if (serialised->size == 0)
serialised->size = instance->size;
serialised->depth = 0;
g_assert (serialised->type_info == instance->type_info);
g_assert (serialised->size == instance->size);
@ -1394,6 +1447,7 @@ test_maybe (void)
serialised.type_info = type_info;
serialised.data = flavoured_malloc (needed_size, flavour);
serialised.size = needed_size;
serialised.depth = 0;
g_variant_serialiser_serialise (serialised,
random_instance_filler,
@ -1516,6 +1570,7 @@ test_array (void)
serialised.type_info = array_info;
serialised.data = flavoured_malloc (needed_size, flavour);
serialised.size = needed_size;
serialised.depth = 0;
g_variant_serialiser_serialise (serialised, random_instance_filler,
(gpointer *) instances, n_children);
@ -1679,6 +1734,7 @@ test_tuple (void)
serialised.type_info = type_info;
serialised.data = flavoured_malloc (needed_size, flavour);
serialised.size = needed_size;
serialised.depth = 0;
g_variant_serialiser_serialise (serialised, random_instance_filler,
(gpointer *) instances, n_children);
@ -1774,6 +1830,7 @@ test_variant (void)
serialised.type_info = type_info;
serialised.data = flavoured_malloc (needed_size, flavour);
serialised.size = needed_size;
serialised.depth = 0;
g_variant_serialiser_serialise (serialised, random_instance_filler,
(gpointer *) &instance, 1);
@ -2055,6 +2112,8 @@ tree_filler (GVariantSerialised *serialised,
if (serialised->type_info == NULL)
serialised->type_info = instance->info;
serialised->depth = 0;
if (instance->data_size == 0)
/* is a container */
{
@ -2225,6 +2284,7 @@ test_byteswap (void)
g_variant_serialised_byteswap (two);
g_assert_cmpmem (one.data, one.size, two.data, two.size);
g_assert_cmpuint (one.depth, ==, two.depth);
tree_instance_free (tree);
g_free (one.data);
@ -4655,6 +4715,108 @@ test_normal_checking_tuples (void)
g_variant_unref (variant);
}
/* Check that deeply nested variants are not considered in normal form when
* deserialised from untrusted data.*/
static void
test_recursion_limits_variant_in_variant (void)
{
GVariant *wrapper_variant = NULL;
gsize i;
GBytes *bytes = NULL;
GVariant *deserialised_variant = NULL;
/* Construct a hierarchy of variants, containing a single string. This is just
* below the maximum recursion level, as a series of nested variant types. */
wrapper_variant = g_variant_new_string ("hello");
for (i = 0; i < G_VARIANT_MAX_RECURSION_DEPTH - 1; i++)
wrapper_variant = g_variant_new_variant (g_steal_pointer (&wrapper_variant));
/* Serialise and deserialise it as untrusted data, to force normalisation. */
bytes = g_variant_get_data_as_bytes (wrapper_variant);
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
bytes, FALSE);
g_assert_nonnull (deserialised_variant);
g_assert_true (g_variant_is_normal_form (deserialised_variant));
g_bytes_unref (bytes);
g_variant_unref (deserialised_variant);
/* Wrap it once more. Normalisation should now fail. */
wrapper_variant = g_variant_new_variant (g_steal_pointer (&wrapper_variant));
bytes = g_variant_get_data_as_bytes (wrapper_variant);
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
bytes, FALSE);
g_assert_nonnull (deserialised_variant);
g_assert_false (g_variant_is_normal_form (deserialised_variant));
g_variant_unref (deserialised_variant);
/* Deserialise it again, but trusted this time. This should succeed. */
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
bytes, TRUE);
g_assert_nonnull (deserialised_variant);
g_assert_true (g_variant_is_normal_form (deserialised_variant));
g_bytes_unref (bytes);
g_variant_unref (deserialised_variant);
g_variant_unref (wrapper_variant);
}
/* Check that deeply nested arrays are not considered in normal form when
* deserialised from untrusted data after being wrapped in a variant. This is
* worth testing, because neither the deeply nested array, nor the variant,
* have a static #GVariantType which is too deep only when nested together do
* they become too deep. */
static void
test_recursion_limits_array_in_variant (void)
{
GVariant *child_variant = NULL;
GVariant *wrapper_variant = NULL;
gsize i;
GBytes *bytes = NULL;
GVariant *deserialised_variant = NULL;
/* Construct a hierarchy of arrays, containing a single string. This is just
* below the maximum recursion level, all in a single definite type. */
child_variant = g_variant_new_string ("hello");
for (i = 0; i < G_VARIANT_MAX_RECURSION_DEPTH - 1; i++)
child_variant = g_variant_new_array (NULL, &child_variant, 1);
/* Serialise and deserialise it as untrusted data, to force normalisation. */
bytes = g_variant_get_data_as_bytes (child_variant);
deserialised_variant = g_variant_new_from_bytes (g_variant_get_type (child_variant),
bytes, FALSE);
g_assert_nonnull (deserialised_variant);
g_assert_true (g_variant_is_normal_form (deserialised_variant));
g_bytes_unref (bytes);
g_variant_unref (deserialised_variant);
/* Wrap it in a variant. Normalisation should now fail. */
wrapper_variant = g_variant_new_variant (g_steal_pointer (&child_variant));
bytes = g_variant_get_data_as_bytes (wrapper_variant);
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
bytes, FALSE);
g_assert_nonnull (deserialised_variant);
g_assert_false (g_variant_is_normal_form (deserialised_variant));
g_variant_unref (deserialised_variant);
/* Deserialise it again, but trusted this time. This should succeed. */
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
bytes, TRUE);
g_assert_nonnull (deserialised_variant);
g_assert_true (g_variant_is_normal_form (deserialised_variant));
g_bytes_unref (bytes);
g_variant_unref (deserialised_variant);
g_variant_unref (wrapper_variant);
}
int
main (int argc, char **argv)
{
@ -4663,6 +4825,10 @@ main (int argc, char **argv)
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/gvariant/type", test_gvarianttype);
g_test_add_func ("/gvariant/type/string-scan/recursion/tuple",
test_gvarianttype_string_scan_recursion_tuple);
g_test_add_func ("/gvariant/type/string-scan/recursion/array",
test_gvarianttype_string_scan_recursion_array);
g_test_add_func ("/gvariant/typeinfo", test_gvarianttypeinfo);
g_test_add_func ("/gvariant/serialiser/maybe", test_maybes);
g_test_add_func ("/gvariant/serialiser/array", test_arrays);
@ -4720,5 +4886,10 @@ main (int argc, char **argv)
g_test_add_func ("/gvariant/normal-checking/tuples",
test_normal_checking_tuples);
g_test_add_func ("/gvariant/recursion-limits/variant-in-variant",
test_recursion_limits_variant_in_variant);
g_test_add_func ("/gvariant/recursion-limits/array-in-variant",
test_recursion_limits_array_in_variant);
return g_test_run ();
}