Skip to content

Commit

Permalink
Merge branch '2121-2540-2794-2797-gvariant-normal-forms' into 'main'
Browse files Browse the repository at this point in the history
Various fixes to normal form handling in GVariant

Closes #2121, #2540, #2794, and #2797

See merge request GNOME/glib!3125
  • Loading branch information
pwithnall committed Dec 13, 2022
2 parents 782b5cb + a70a16b commit 4eb9b09
Show file tree
Hide file tree
Showing 6 changed files with 1,124 additions and 168 deletions.
185 changes: 161 additions & 24 deletions glib/gvariant-core.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright © 2007, 2008 Ryan Lortie
* Copyright © 2010 Codethink Limited
* Copyright © 2022 Endless OS Foundation, LLC
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
Expand Down Expand Up @@ -67,6 +68,8 @@ struct _GVariant
{
GBytes *bytes;
gconstpointer data;
gsize ordered_offsets_up_to;
gsize checked_offsets_up_to;
} serialised;

struct
Expand Down Expand Up @@ -164,6 +167,42 @@ struct _GVariant
* if .data pointed to the appropriate number of nul
* bytes.
*
* .ordered_offsets_up_to: If ordered_offsets_up_to == n this means that all
* the frame offsets up to and including the frame
* offset determining the end of element n are in
* order. This guarantees that the bytes of element
* n don't overlap with any previous element.
*
* For trusted data this is set to G_MAXSIZE and we
* don't check that the frame offsets are in order.
*
* Note: This doesn't imply the offsets are good in
* any way apart from their ordering. In particular
* offsets may be out of bounds for this value or
* may imply that the data overlaps the frame
* offsets themselves.
*
* This field is only relevant for arrays of non
* fixed width types and for tuples.
*
* .checked_offsets_up_to: Similarly to .ordered_offsets_up_to, this stores
* the index of the highest element, n, whose frame
* offsets (and all the preceding frame offsets)
* have been checked for validity.
*
* It is always the case that
* .checked_offsets_up_to ≥ .ordered_offsets_up_to.
*
* If .checked_offsets_up_to == .ordered_offsets_up_to,
* then a bad offset has not been found so far.
*
* If .checked_offsets_up_to > .ordered_offsets_up_to,
* then a bad offset has been found at
* (.ordered_offsets_up_to + 1).
*
* This field is only relevant for arrays of non
* fixed width types and for tuples.
*
* .tree: Only valid when the instance is in tree form.
*
* Note that accesses from other threads could result in
Expand Down Expand Up @@ -351,6 +390,29 @@ g_variant_ensure_size (GVariant *value)
}
}

/* < private >
* g_variant_to_serialised:
* @value: a #GVariant
*
* Gets a GVariantSerialised for a GVariant in state STATE_SERIALISED.
*/
inline static GVariantSerialised
g_variant_to_serialised (GVariant *value)
{
g_assert (value->state & STATE_SERIALISED);
{
GVariantSerialised serialised = {
value->type_info,
(gpointer) value->contents.serialised.data,
value->size,
value->depth,
value->contents.serialised.ordered_offsets_up_to,
value->contents.serialised.checked_offsets_up_to,
};
return serialised;
}
}

/* < private >
* g_variant_serialise:
* @value: a #GVariant
Expand All @@ -377,6 +439,8 @@ g_variant_serialise (GVariant *value,
serialised.size = value->size;
serialised.data = data;
serialised.depth = value->depth;
serialised.ordered_offsets_up_to = 0;
serialised.checked_offsets_up_to = 0;

children = (gpointer *) value->contents.tree.children;
n_children = value->contents.tree.n_children;
Expand Down Expand Up @@ -420,6 +484,17 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
g_assert (serialised->size == value->size);
serialised->depth = value->depth;

if (value->state & STATE_SERIALISED)
{
serialised->ordered_offsets_up_to = value->contents.serialised.ordered_offsets_up_to;
serialised->checked_offsets_up_to = value->contents.serialised.checked_offsets_up_to;
}
else
{
serialised->ordered_offsets_up_to = 0;
serialised->checked_offsets_up_to = 0;
}

if (serialised->data)
/* g_variant_store() is a public API, so it
* it will reacquire the lock if it needs to.
Expand Down Expand Up @@ -462,6 +537,8 @@ g_variant_ensure_serialised (GVariant *value)
bytes = g_bytes_new_take (data, value->size);
value->contents.serialised.data = g_bytes_get_data (bytes, NULL);
value->contents.serialised.bytes = bytes;
value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE;
value->contents.serialised.checked_offsets_up_to = G_MAXSIZE;
value->state |= STATE_SERIALISED;
}
}
Expand Down Expand Up @@ -542,6 +619,8 @@ g_variant_new_from_bytes (const GVariantType *type,
serialised.type_info = value->type_info;
serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size);
serialised.depth = 0;
serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0;

if (!g_variant_serialised_check (serialised))
{
Expand Down Expand Up @@ -592,6 +671,9 @@ g_variant_new_from_bytes (const GVariantType *type,
value->contents.serialised.data = g_bytes_get_data (bytes, &value->size);
}

value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
value->contents.serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0;

g_clear_pointer (&owned_bytes, g_bytes_unref);

return value;
Expand Down Expand Up @@ -1009,16 +1091,8 @@ g_variant_n_children (GVariant *value)
g_variant_lock (value);

if (value->state & STATE_SERIALISED)
{
GVariantSerialised serialised = {
value->type_info,
(gpointer) value->contents.serialised.data,
value->size,
value->depth,
};

n_children = g_variant_serialised_n_children (serialised);
}
n_children = g_variant_serialised_n_children (
g_variant_to_serialised (value));
else
n_children = value->contents.tree.n_children;

Expand Down Expand Up @@ -1085,12 +1159,7 @@ g_variant_get_child_value (GVariant *value,
}

{
GVariantSerialised serialised = {
value->type_info,
(gpointer) value->contents.serialised.data,
value->size,
value->depth,
};
GVariantSerialised serialised = g_variant_to_serialised (value);
GVariantSerialised s_child;
GVariant *child;

Expand All @@ -1099,6 +1168,10 @@ g_variant_get_child_value (GVariant *value,
*/
s_child = g_variant_serialised_get_child (serialised, index_);

/* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
value->contents.serialised.checked_offsets_up_to = MAX (value->contents.serialised.checked_offsets_up_to, serialised.checked_offsets_up_to);

/* 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
Expand All @@ -1110,6 +1183,7 @@ g_variant_get_child_value (GVariant *value,
G_VARIANT_MAX_RECURSION_DEPTH - value->depth)
{
g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT));
g_variant_type_info_unref (s_child.type_info);
return g_variant_new_tuple (NULL, 0);
}

Expand All @@ -1124,11 +1198,81 @@ g_variant_get_child_value (GVariant *value,
child->contents.serialised.bytes =
g_bytes_ref (value->contents.serialised.bytes);
child->contents.serialised.data = s_child.data;
child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to;

return child;
}
}

/**
* g_variant_maybe_get_child_value:
* @value: a container #GVariant
* @index_: the index of the child to fetch
*
* Reads a child item out of a container #GVariant instance, if it is in normal
* form. If it is not in normal form, return %NULL.
*
* This function behaves the same as g_variant_get_child_value(), except that it
* returns %NULL if the child is not in normal form. g_variant_get_child_value()
* would instead return a new default value of the correct type.
*
* This is intended to be used internally to avoid unnecessary #GVariant
* allocations.
*
* The returned value is never floating. You should free it with
* g_variant_unref() when you're done with it.
*
* This function is O(1).
*
* Returns: (transfer full): the child at the specified index
*
* Since: 2.74
*/
GVariant *
g_variant_maybe_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)
{
g_variant_lock (value);

if (~value->state & STATE_SERIALISED)
{
GVariant *child;

child = g_variant_ref (value->contents.tree.children[index_]);
g_variant_unlock (value);

return child;
}

g_variant_unlock (value);
}

{
GVariantSerialised serialised = g_variant_to_serialised (value);
GVariantSerialised s_child;

/* get the serializer to extract the serialized data for the child
* from the serialized data for the container
*/
s_child = g_variant_serialised_get_child (serialised, index_);

if (!(value->state & STATE_TRUSTED) && s_child.data == NULL)
{
g_variant_type_info_unref (s_child.type_info);
return NULL;
}

g_variant_type_info_unref (s_child.type_info);
return g_variant_get_child_value (value, index_);
}
}

/**
* g_variant_store:
* @value: the #GVariant to store
Expand Down Expand Up @@ -1203,14 +1347,7 @@ g_variant_is_normal_form (GVariant *value)

if (value->state & STATE_SERIALISED)
{
GVariantSerialised serialised = {
value->type_info,
(gpointer) value->contents.serialised.data,
value->size,
value->depth
};

if (g_variant_serialised_is_normal (serialised))
if (g_variant_serialised_is_normal (g_variant_to_serialised (value)))
value->state |= STATE_TRUSTED;
}
else
Expand Down
3 changes: 3 additions & 0 deletions glib/gvariant-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ GVariantTypeInfo * g_variant_get_type_info (GVarian

gsize g_variant_get_depth (GVariant *value);

GVariant * g_variant_maybe_get_child_value (GVariant *value,
gsize index_);

#endif /* __G_VARIANT_CORE_H__ */

0 comments on commit 4eb9b09

Please sign in to comment.