gvariant: Check array offsets against serialised data length

When getting a child from a serialised variable array, check its offset
against the length of the serialised data of the array (excluding the
length of the offset table). The offset was already checked against the
length of the entire serialised array (including the offset table) — but a
child should not be able to start inside the offset table.

A test is included.

oss-fuzz#9803

Signed-off-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
Philip Withnall 2018-09-07 22:26:05 +01:00
parent a95e9f79d8
commit fe9564feda
2 changed files with 27 additions and 1 deletions

View File

@ -694,7 +694,7 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
(offset_size * index_),
offset_size);
if (start < end && end <= value.size)
if (start < end && end <= value.size && end <= last_end)
{
child.data = value.data + start;
child.size = end - start;

View File

@ -4785,6 +4785,30 @@ test_recursion_limits_array_in_variant (void)
g_variant_unref (wrapper_variant);
}
/* Test that an array with invalidly large values in its offset table is
* normalised successfully without looping infinitely. */
static void
test_normal_checking_array_offsets (void)
{
const guint8 data[] = {
0x07, 0xe5, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00,
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'g',
};
gsize size = sizeof (data);
GVariant *variant = NULL;
GVariant *normal_variant = NULL;
variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size,
FALSE, NULL, NULL);
g_assert_nonnull (variant);
normal_variant = g_variant_get_normal_form (variant);
g_assert_nonnull (normal_variant);
g_variant_unref (normal_variant);
g_variant_unref (variant);
}
int
main (int argc, char **argv)
{
@ -4853,6 +4877,8 @@ main (int argc, char **argv)
g_test_add_func ("/gvariant/normal-checking/tuples",
test_normal_checking_tuples);
g_test_add_func ("/gvariant/normal-checking/array-offsets",
test_normal_checking_array_offsets);
g_test_add_func ("/gvariant/recursion-limits/variant-in-variant",
test_recursion_limits_variant_in_variant);