gvariant: Handle empty serialisations in get_child_value()

When g_variant_get_child_value() is called for a child whose
serialisation is an empty byte string (which is possible), `bytes_data`
will be non-`NULL`, but `data` may be `NULL`. This results in a negative
offset being passed to `g_bytes_new_from_bytes()`, and a critical
warning.

So if `data` is `NULL`, set it to point to `bytes_data` so the offset is
calculated as zero. The actual value of the offset doesn’t matter, since
in this situation the size is always zero. An offset of zero is never
going to cause problems.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
Fixes: #1865
This commit is contained in:
Philip Withnall 2019-08-20 15:17:40 +03:00
parent 10be2c12ab
commit efe5b70192
2 changed files with 48 additions and 0 deletions

View File

@ -949,6 +949,12 @@ g_variant_get_data_as_bytes (GVariant *value)
data = value->contents.serialised.data;
size = value->size;
if (data == NULL)
{
g_assert (size == 0);
data = bytes_data;
}
if (data == bytes_data && size == bytes_size)
return g_bytes_ref (value->contents.serialised.bytes);
else

View File

@ -2303,6 +2303,46 @@ test_byteswaps (void)
g_variant_type_info_assert_no_infos ();
}
static void
test_serialiser_children (void)
{
GBytes *data1, *data2;
GVariant *child1, *child2;
GVariantType *mv_type = g_variant_type_new_maybe (G_VARIANT_TYPE_VARIANT);
GVariant *variant, *child;
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1865");
g_test_summary ("Test that getting a child variant before and after "
"serialisation of the parent works");
/* Construct a variable sized array containing a child which serialises to a
* zero-length bytestring. */
child = g_variant_new_maybe (G_VARIANT_TYPE_VARIANT, NULL);
variant = g_variant_new_array (mv_type, &child, 1);
/* Get the child before serialising. */
child1 = g_variant_get_child_value (variant, 0);
data1 = g_variant_get_data_as_bytes (child1);
/* Serialise the parent variant. */
g_variant_get_data (variant);
/* Get the child again after serialising — this uses a different code path. */
child2 = g_variant_get_child_value (variant, 0);
data2 = g_variant_get_data_as_bytes (child2);
/* Check things are equal. */
g_assert_cmpvariant (child1, child2);
g_assert_true (g_bytes_equal (data1, data2));
g_variant_unref (child2);
g_variant_unref (child1);
g_variant_unref (variant);
g_bytes_unref (data2);
g_bytes_unref (data1);
g_variant_type_free (mv_type);
}
static void
test_fuzz (gdouble *fuzziness)
{
@ -5075,6 +5115,7 @@ main (int argc, char **argv)
guint i;
g_test_init (&argc, &argv, NULL);
g_test_bug_base ("");
g_test_add_func ("/gvariant/type", test_gvarianttype);
g_test_add_func ("/gvariant/type/string-scan/recursion/tuple",
@ -5088,6 +5129,7 @@ main (int argc, char **argv)
g_test_add_func ("/gvariant/serialiser/variant", test_variants);
g_test_add_func ("/gvariant/serialiser/strings", test_strings);
g_test_add_func ("/gvariant/serialiser/byteswap", test_byteswaps);
g_test_add_func ("/gvariant/serialiser/children", test_serialiser_children);
for (i = 1; i <= 20; i += 4)
{