mirror of
https://git.yoctoproject.org/poky
synced 2026-04-21 21:32:12 +02:00
glib-2.0: Fix multiple vulnerabilities
CVE's Fixed: CVE-2023-29499: glib: GVariant offset table entry size is not checked in is_normal() CVE-2023-32611: glib: g_variant_byteswap() can take a long time with some non-normal inputs CVE-2023-32636: glib: Timeout in fuzz_variant_text CVE-2023-32643: glib: Heap-buffer-overflow in g_variant_serialised_get_child CVE-2023-32665: glib: GVariant deserialisation does not match spec for non-normal data (From OE-Core rev: b576beba80d44e67762d46bf3bc2f14c05bc0f6b) Signed-off-by: Siddharth Doshi <sdoshi@mvista.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
This commit is contained in:
committed by
Steve Sakoman
parent
8ae21cd487
commit
aa99487732
290
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch
Normal file
290
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch
Normal file
@@ -0,0 +1,290 @@
|
||||
From 5f4485c4ff57fdefb1661531788def7ca5a47328 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 04:19:44 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Check offset table entry size is minimal
|
||||
|
||||
The entries in an offset table (which is used for variable sized arrays
|
||||
and tuples containing variable sized members) are sized so that they can
|
||||
address every byte in the overall variant.
|
||||
|
||||
The specification requires that for a variant to be in normal form, its
|
||||
offset table entries must be the minimum width such that they can
|
||||
address every byte in the variant.
|
||||
|
||||
That minimality requirement was not checked in
|
||||
`g_variant_is_normal_form()`, leading to two different byte arrays being
|
||||
interpreted as the normal form of a given variant tree. That kind of
|
||||
confusion could potentially be exploited, and is certainly a bug.
|
||||
|
||||
Fix it by adding the necessary checks on offset table entry width, and
|
||||
unit tests.
|
||||
|
||||
Spotted by William Manley.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Fixes: #2794
|
||||
|
||||
CVE: CVE-2023-29499
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/5f4485c4ff57fdefb1661531788def7ca5a47328]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 19 +++-
|
||||
glib/tests/gvariant.c | 176 +++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 194 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index 0bf7243..5aa2cbc 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -694,6 +694,10 @@ gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value)
|
||||
out.data_size = last_end;
|
||||
out.array = value.data + last_end;
|
||||
out.length = offsets_array_size / out.offset_size;
|
||||
+
|
||||
+ if (out.length > 0 && gvs_calculate_total_size (last_end, out.length) != value.size)
|
||||
+ return out; /* offset size not minimal */
|
||||
+
|
||||
out.is_normal = TRUE;
|
||||
|
||||
return out;
|
||||
@@ -1201,6 +1205,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
gsize length;
|
||||
gsize offset;
|
||||
gsize i;
|
||||
+ gsize offset_table_size;
|
||||
|
||||
/* as per the comment in gvs_tuple_get_child() */
|
||||
if G_UNLIKELY (value.data == NULL && value.size != 0)
|
||||
@@ -1305,7 +1310,19 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
}
|
||||
}
|
||||
|
||||
- return offset_ptr == offset;
|
||||
+ /* @offset_ptr has been counting backwards from the end of the variant, to
|
||||
+ * find the beginning of the offset table. @offset has been counting forwards
|
||||
+ * from the beginning of the variant to find the end of the data. They should
|
||||
+ * have met in the middle. */
|
||||
+ if (offset_ptr != offset)
|
||||
+ return FALSE;
|
||||
+
|
||||
+ offset_table_size = value.size - offset_ptr;
|
||||
+ if (value.size > 0 &&
|
||||
+ gvs_calculate_total_size (offset, offset_table_size / offset_size) != value.size)
|
||||
+ return FALSE; /* offset size not minimal */
|
||||
+
|
||||
+ return TRUE;
|
||||
}
|
||||
|
||||
/* Variants {{{2
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index d640c81..4ce0e4f 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -5092,6 +5092,86 @@ test_normal_checking_array_offsets2 (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* Test that an otherwise-valid serialised GVariant is considered non-normal if
|
||||
+ * its offset table entries are too wide.
|
||||
+ *
|
||||
+ * See §2.3.6 (Framing Offsets) of the GVariant specification. */
|
||||
+static void
|
||||
+test_normal_checking_array_offsets_minimal_sized (void)
|
||||
+{
|
||||
+ GVariantBuilder builder;
|
||||
+ gsize i;
|
||||
+ GVariant *aay_constructed = NULL;
|
||||
+ const guint8 *data = NULL;
|
||||
+ guint8 *data_owned = NULL;
|
||||
+ GVariant *aay_deserialised = NULL;
|
||||
+ GVariant *aay_normalised = NULL;
|
||||
+
|
||||
+ /* Construct an array of type aay, consisting of 128 elements which are each
|
||||
+ * an empty array, i.e. `[[] * 128]`. This is chosen because the inner
|
||||
+ * elements are variable sized (making the outer array variable sized, so it
|
||||
+ * must have an offset table), but they are also zero-sized when serialised.
|
||||
+ * So the serialised representation of @aay_constructed consists entirely of
|
||||
+ * its offset table, which is entirely zeroes.
|
||||
+ *
|
||||
+ * The array is chosen to be 128 elements long because that means offset
|
||||
+ * table entries which are 1 byte long. If the elements in the array were
|
||||
+ * non-zero-sized (to the extent that the overall array is ≥256 bytes long),
|
||||
+ * the offset table entries would end up being 2 bytes long. */
|
||||
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
|
||||
+
|
||||
+ for (i = 0; i < 128; i++)
|
||||
+ g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0));
|
||||
+
|
||||
+ aay_constructed = g_variant_builder_end (&builder);
|
||||
+
|
||||
+ /* Verify that the constructed array is in normal form, and its serialised
|
||||
+ * form is `b'\0' * 128`. */
|
||||
+ g_assert_true (g_variant_is_normal_form (aay_constructed));
|
||||
+ g_assert_cmpuint (g_variant_n_children (aay_constructed), ==, 128);
|
||||
+ g_assert_cmpuint (g_variant_get_size (aay_constructed), ==, 128);
|
||||
+
|
||||
+ data = g_variant_get_data (aay_constructed);
|
||||
+ for (i = 0; i < g_variant_get_size (aay_constructed); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ /* Construct a serialised `aay` GVariant which is `b'\0' * 256`. This has to
|
||||
+ * be a non-normal form of `[[] * 128]`, with 2-byte-long offset table
|
||||
+ * entries, because each offset table entry has to be able to reference all of
|
||||
+ * the byte boundaries in the container. All the entries in the offset table
|
||||
+ * are zero, so all the elements of the array are zero-sized. */
|
||||
+ data = data_owned = g_malloc0 (256);
|
||||
+ aay_deserialised = g_variant_new_from_data (G_VARIANT_TYPE ("aay"),
|
||||
+ data,
|
||||
+ 256,
|
||||
+ FALSE,
|
||||
+ g_free,
|
||||
+ g_steal_pointer (&data_owned));
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (aay_deserialised));
|
||||
+ g_assert_cmpuint (g_variant_n_children (aay_deserialised), ==, 128);
|
||||
+ g_assert_cmpuint (g_variant_get_size (aay_deserialised), ==, 256);
|
||||
+
|
||||
+ data = g_variant_get_data (aay_deserialised);
|
||||
+ for (i = 0; i < g_variant_get_size (aay_deserialised); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ /* Get its normal form. That should change the serialised size. */
|
||||
+ aay_normalised = g_variant_get_normal_form (aay_deserialised);
|
||||
+
|
||||
+ g_assert_true (g_variant_is_normal_form (aay_normalised));
|
||||
+ g_assert_cmpuint (g_variant_n_children (aay_normalised), ==, 128);
|
||||
+ g_assert_cmpuint (g_variant_get_size (aay_normalised), ==, 128);
|
||||
+
|
||||
+ data = g_variant_get_data (aay_normalised);
|
||||
+ for (i = 0; i < g_variant_get_size (aay_normalised); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ g_variant_unref (aay_normalised);
|
||||
+ g_variant_unref (aay_deserialised);
|
||||
+ g_variant_unref (aay_constructed);
|
||||
+}
|
||||
+
|
||||
/* Test that a tuple with invalidly large values in its offset table is
|
||||
* normalised successfully without looping infinitely. */
|
||||
static void
|
||||
@@ -5286,6 +5366,98 @@ test_normal_checking_tuple_offsets4 (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* Test that an otherwise-valid serialised GVariant is considered non-normal if
|
||||
+ * its offset table entries are too wide.
|
||||
+ *
|
||||
+ * See §2.3.6 (Framing Offsets) of the GVariant specification. */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets_minimal_sized (void)
|
||||
+{
|
||||
+ GString *type_string = NULL;
|
||||
+ GVariantBuilder builder;
|
||||
+ gsize i;
|
||||
+ GVariant *ray_constructed = NULL;
|
||||
+ const guint8 *data = NULL;
|
||||
+ guint8 *data_owned = NULL;
|
||||
+ GVariant *ray_deserialised = NULL;
|
||||
+ GVariant *ray_normalised = NULL;
|
||||
+
|
||||
+ /* Construct a tuple of type (ay…ay), consisting of 129 members which are each
|
||||
+ * an empty array, i.e. `([] * 129)`. This is chosen because the inner
|
||||
+ * members are variable sized, so the outer tuple must have an offset table,
|
||||
+ * but they are also zero-sized when serialised. So the serialised
|
||||
+ * representation of @ray_constructed consists entirely of its offset table,
|
||||
+ * which is entirely zeroes.
|
||||
+ *
|
||||
+ * The tuple is chosen to be 129 members long because that means it has 128
|
||||
+ * offset table entries which are 1 byte long each. If the members in the
|
||||
+ * tuple were non-zero-sized (to the extent that the overall tuple is ≥256
|
||||
+ * bytes long), the offset table entries would end up being 2 bytes long.
|
||||
+ *
|
||||
+ * 129 members are used unlike 128 array elements in
|
||||
+ * test_normal_checking_array_offsets_minimal_sized(), because the last member
|
||||
+ * in a tuple never needs an offset table entry. */
|
||||
+ type_string = g_string_new ("");
|
||||
+ g_string_append_c (type_string, '(');
|
||||
+ for (i = 0; i < 129; i++)
|
||||
+ g_string_append (type_string, "ay");
|
||||
+ g_string_append_c (type_string, ')');
|
||||
+
|
||||
+ g_variant_builder_init (&builder, G_VARIANT_TYPE (type_string->str));
|
||||
+
|
||||
+ for (i = 0; i < 129; i++)
|
||||
+ g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0));
|
||||
+
|
||||
+ ray_constructed = g_variant_builder_end (&builder);
|
||||
+
|
||||
+ /* Verify that the constructed tuple is in normal form, and its serialised
|
||||
+ * form is `b'\0' * 128`. */
|
||||
+ g_assert_true (g_variant_is_normal_form (ray_constructed));
|
||||
+ g_assert_cmpuint (g_variant_n_children (ray_constructed), ==, 129);
|
||||
+ g_assert_cmpuint (g_variant_get_size (ray_constructed), ==, 128);
|
||||
+
|
||||
+ data = g_variant_get_data (ray_constructed);
|
||||
+ for (i = 0; i < g_variant_get_size (ray_constructed); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ /* Construct a serialised `(ay…ay)` GVariant which is `b'\0' * 256`. This has
|
||||
+ * to be a non-normal form of `([] * 129)`, with 2-byte-long offset table
|
||||
+ * entries, because each offset table entry has to be able to reference all of
|
||||
+ * the byte boundaries in the container. All the entries in the offset table
|
||||
+ * are zero, so all the members of the tuple are zero-sized. */
|
||||
+ data = data_owned = g_malloc0 (256);
|
||||
+ ray_deserialised = g_variant_new_from_data (G_VARIANT_TYPE (type_string->str),
|
||||
+ data,
|
||||
+ 256,
|
||||
+ FALSE,
|
||||
+ g_free,
|
||||
+ g_steal_pointer (&data_owned));
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (ray_deserialised));
|
||||
+ g_assert_cmpuint (g_variant_n_children (ray_deserialised), ==, 129);
|
||||
+ g_assert_cmpuint (g_variant_get_size (ray_deserialised), ==, 256);
|
||||
+
|
||||
+ data = g_variant_get_data (ray_deserialised);
|
||||
+ for (i = 0; i < g_variant_get_size (ray_deserialised); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ /* Get its normal form. That should change the serialised size. */
|
||||
+ ray_normalised = g_variant_get_normal_form (ray_deserialised);
|
||||
+
|
||||
+ g_assert_true (g_variant_is_normal_form (ray_normalised));
|
||||
+ g_assert_cmpuint (g_variant_n_children (ray_normalised), ==, 129);
|
||||
+ g_assert_cmpuint (g_variant_get_size (ray_normalised), ==, 128);
|
||||
+
|
||||
+ data = g_variant_get_data (ray_normalised);
|
||||
+ for (i = 0; i < g_variant_get_size (ray_normalised); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ g_variant_unref (ray_normalised);
|
||||
+ g_variant_unref (ray_deserialised);
|
||||
+ g_variant_unref (ray_constructed);
|
||||
+ g_string_free (type_string, TRUE);
|
||||
+}
|
||||
+
|
||||
/* Test that an empty object path is normalised successfully to the base object
|
||||
* path, ‘/’. */
|
||||
static void
|
||||
@@ -5431,6 +5603,8 @@ main (int argc, char **argv)
|
||||
test_normal_checking_array_offsets);
|
||||
g_test_add_func ("/gvariant/normal-checking/array-offsets2",
|
||||
test_normal_checking_array_offsets2);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/array-offsets/minimal-sized",
|
||||
+ test_normal_checking_array_offsets_minimal_sized);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
|
||||
test_normal_checking_tuple_offsets);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets2",
|
||||
@@ -5439,6 +5613,8 @@ main (int argc, char **argv)
|
||||
test_normal_checking_tuple_offsets3);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
|
||||
test_normal_checking_tuple_offsets4);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized",
|
||||
+ test_normal_checking_tuple_offsets_minimal_sized);
|
||||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||||
test_normal_checking_empty_object_path);
|
||||
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
From 1deacdd4e8e35a5cf1417918ca4f6b0afa6409b1 Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 9 Aug 2023 10:04:49 +0000
|
||||
Subject: [PATCH] gvariant-core: Consolidate construction of
|
||||
`GVariantSerialised`
|
||||
|
||||
So I only need to change it in one place.
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/1deacdd4e8e35a5cf1417918ca4f6b0afa6409b1]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant.c | 8 +++++---
|
||||
glib/tests/gvariant.c | 24 ++++++++++++++++++++++++
|
||||
2 files changed, 29 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index 8ba701e..4dbd9e8 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5952,14 +5952,16 @@ g_variant_byteswap (GVariant *value)
|
||||
g_variant_serialised_byteswap (serialised);
|
||||
|
||||
bytes = g_bytes_new_take (serialised.data, serialised.size);
|
||||
- new = g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE);
|
||||
+ new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE));
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
else
|
||||
/* contains no multi-byte data */
|
||||
- new = value;
|
||||
+ new = g_variant_get_normal_form (value);
|
||||
|
||||
- return g_variant_ref_sink (new);
|
||||
+ g_assert (g_variant_is_trusted (new));
|
||||
+
|
||||
+ return g_steal_pointer (&new);
|
||||
}
|
||||
|
||||
/**
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 4ce0e4f..3dda08e 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -3834,6 +3834,29 @@ test_gv_byteswap (void)
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
+static void
|
||||
+test_gv_byteswap_non_normal_non_aligned (void)
|
||||
+{
|
||||
+ const guint8 data[] = { 0x02 };
|
||||
+ GVariant *v = NULL;
|
||||
+ GVariant *v_byteswapped = NULL;
|
||||
+
|
||||
+ g_test_summary ("Test that calling g_variant_byteswap() on a variant which "
|
||||
+ "is in non-normal form and doesn’t need byteswapping returns "
|
||||
+ "the same variant in normal form.");
|
||||
+
|
||||
+ v = g_variant_new_from_data (G_VARIANT_TYPE_BOOLEAN, data, sizeof (data), FALSE, NULL, NULL);
|
||||
+ g_assert_false (g_variant_is_normal_form (v));
|
||||
+
|
||||
+ v_byteswapped = g_variant_byteswap (v);
|
||||
+ g_assert_true (g_variant_is_normal_form (v_byteswapped));
|
||||
+
|
||||
+ g_assert_cmpvariant (v, v_byteswapped);
|
||||
+
|
||||
+ g_variant_unref (v);
|
||||
+ g_variant_unref (v_byteswapped);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
test_parser (void)
|
||||
{
|
||||
@@ -5570,6 +5593,7 @@ main (int argc, char **argv)
|
||||
g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
|
||||
g_test_add_func ("/gvariant/hashing", test_hashing);
|
||||
g_test_add_func ("/gvariant/byteswap", test_gv_byteswap);
|
||||
+ g_test_add_func ("/gvariant/byteswap/non-normal-non-aligned", test_gv_byteswap_non_normal_non_aligned);
|
||||
g_test_add_func ("/gvariant/parser", test_parses);
|
||||
g_test_add_func ("/gvariant/parser/integer-bounds", test_parser_integer_bounds);
|
||||
g_test_add_func ("/gvariant/parser/recursion", test_parser_recursion);
|
||||
--
|
||||
2.24.4
|
||||
|
||||
255
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0002.patch
Normal file
255
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0002.patch
Normal file
@@ -0,0 +1,255 @@
|
||||
From 446e69f5edd72deb2196dee36bbaf8056caf6948 Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 9 Aug 2023 10:39:34 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Factor out functions for dealing with
|
||||
framing offsets
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/446e69f5edd72deb2196dee36bbaf8056caf6948]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant.c | 81 +++++++++++++++++++++++++++++++++----------
|
||||
glib/tests/gvariant.c | 57 ++++++++++++++++++++++++++----
|
||||
2 files changed, 112 insertions(+), 26 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index 4dbd9e8..a80c2c9 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5788,7 +5788,8 @@ g_variant_iter_loop (GVariantIter *iter,
|
||||
|
||||
/* Serialised data {{{1 */
|
||||
static GVariant *
|
||||
-g_variant_deep_copy (GVariant *value)
|
||||
+g_variant_deep_copy (GVariant *value,
|
||||
+ gboolean byteswap)
|
||||
{
|
||||
switch (g_variant_classify (value))
|
||||
{
|
||||
@@ -5806,7 +5807,7 @@ g_variant_deep_copy (GVariant *value)
|
||||
for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++)
|
||||
{
|
||||
GVariant *child = g_variant_get_child_value (value, i);
|
||||
- g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
|
||||
+ g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap));
|
||||
g_variant_unref (child);
|
||||
}
|
||||
|
||||
@@ -5820,28 +5821,63 @@ g_variant_deep_copy (GVariant *value)
|
||||
return g_variant_new_byte (g_variant_get_byte (value));
|
||||
|
||||
case G_VARIANT_CLASS_INT16:
|
||||
- return g_variant_new_int16 (g_variant_get_int16 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_int16 (GUINT16_SWAP_LE_BE (g_variant_get_int16 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_int16 (g_variant_get_int16 (value));
|
||||
|
||||
case G_VARIANT_CLASS_UINT16:
|
||||
- return g_variant_new_uint16 (g_variant_get_uint16 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_uint16 (GUINT16_SWAP_LE_BE (g_variant_get_uint16 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_uint16 (g_variant_get_uint16 (value));
|
||||
|
||||
case G_VARIANT_CLASS_INT32:
|
||||
- return g_variant_new_int32 (g_variant_get_int32 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_int32 (GUINT32_SWAP_LE_BE (g_variant_get_int32 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_int32 (g_variant_get_int32 (value));
|
||||
|
||||
case G_VARIANT_CLASS_UINT32:
|
||||
- return g_variant_new_uint32 (g_variant_get_uint32 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_uint32 (GUINT32_SWAP_LE_BE (g_variant_get_uint32 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_uint32 (g_variant_get_uint32 (value));
|
||||
|
||||
case G_VARIANT_CLASS_INT64:
|
||||
- return g_variant_new_int64 (g_variant_get_int64 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_int64 (GUINT64_SWAP_LE_BE (g_variant_get_int64 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_int64 (g_variant_get_int64 (value));
|
||||
|
||||
case G_VARIANT_CLASS_UINT64:
|
||||
- return g_variant_new_uint64 (g_variant_get_uint64 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_uint64 (GUINT64_SWAP_LE_BE (g_variant_get_uint64 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_uint64 (g_variant_get_uint64 (value));
|
||||
|
||||
case G_VARIANT_CLASS_HANDLE:
|
||||
- return g_variant_new_handle (g_variant_get_handle (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_handle (GUINT32_SWAP_LE_BE (g_variant_get_handle (value)));
|
||||
+ else
|
||||
+ return g_variant_new_handle (g_variant_get_handle (value));
|
||||
|
||||
case G_VARIANT_CLASS_DOUBLE:
|
||||
- return g_variant_new_double (g_variant_get_double (value));
|
||||
+ if (byteswap)
|
||||
+ {
|
||||
+ /* We have to convert the double to a uint64 here using a union,
|
||||
+ * because a cast will round it numerically. */
|
||||
+ union
|
||||
+ {
|
||||
+ guint64 u64;
|
||||
+ gdouble dbl;
|
||||
+ } u1, u2;
|
||||
+ u1.dbl = g_variant_get_double (value);
|
||||
+ u2.u64 = GUINT64_SWAP_LE_BE (u1.u64);
|
||||
+ return g_variant_new_double (u2.dbl);
|
||||
+ }
|
||||
+ else
|
||||
+ return g_variant_new_double (g_variant_get_double (value));
|
||||
|
||||
case G_VARIANT_CLASS_STRING:
|
||||
return g_variant_new_string (g_variant_get_string (value, NULL));
|
||||
@@ -5896,7 +5932,7 @@ g_variant_get_normal_form (GVariant *value)
|
||||
if (g_variant_is_normal_form (value))
|
||||
return g_variant_ref (value);
|
||||
|
||||
- trusted = g_variant_deep_copy (value);
|
||||
+ trusted = g_variant_deep_copy (value, FALSE);
|
||||
g_assert (g_variant_is_trusted (trusted));
|
||||
|
||||
return g_variant_ref_sink (trusted);
|
||||
@@ -5916,6 +5952,11 @@ g_variant_get_normal_form (GVariant *value)
|
||||
* contain multi-byte numeric data. That include strings, booleans,
|
||||
* bytes and containers containing only these things (recursively).
|
||||
*
|
||||
+ * While this function can safely handle untrusted, non-normal data, it is
|
||||
+ * recommended to check whether the input is in normal form beforehand, using
|
||||
+ * g_variant_is_normal_form(), and to reject non-normal inputs if your
|
||||
+ * application can be strict about what inputs it rejects.
|
||||
+ *
|
||||
* The returned value is always in normal form and is marked as trusted.
|
||||
*
|
||||
* Returns: (transfer full): the byteswapped form of @value
|
||||
@@ -5933,21 +5974,20 @@ g_variant_byteswap (GVariant *value)
|
||||
|
||||
g_variant_type_info_query (type_info, &alignment, NULL);
|
||||
|
||||
- if (alignment)
|
||||
- /* (potentially) contains multi-byte numeric data */
|
||||
+ if (alignment && g_variant_is_normal_form (value))
|
||||
{
|
||||
+ /* (potentially) contains multi-byte numeric data, but is also already in
|
||||
+ * normal form so we can use a faster byteswapping codepath on the
|
||||
+ * serialised data */
|
||||
GVariantSerialised serialised = { 0, };
|
||||
- GVariant *trusted;
|
||||
GBytes *bytes;
|
||||
|
||||
- trusted = g_variant_get_normal_form (value);
|
||||
- serialised.type_info = g_variant_get_type_info (trusted);
|
||||
- serialised.size = g_variant_get_size (trusted);
|
||||
+ serialised.type_info = g_variant_get_type_info (value);
|
||||
+ serialised.size = g_variant_get_size (value);
|
||||
serialised.data = g_malloc (serialised.size);
|
||||
serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */
|
||||
serialised.checked_offsets_up_to = G_MAXSIZE;
|
||||
- g_variant_store (trusted, serialised.data);
|
||||
- g_variant_unref (trusted);
|
||||
+ g_variant_store (value, serialised.data);
|
||||
|
||||
g_variant_serialised_byteswap (serialised);
|
||||
|
||||
@@ -5955,6 +5995,9 @@ g_variant_byteswap (GVariant *value)
|
||||
new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE));
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
+ else if (alignment)
|
||||
+ /* (potentially) contains multi-byte numeric data */
|
||||
+ new = g_variant_ref_sink (g_variant_deep_copy (value, TRUE));
|
||||
else
|
||||
/* contains no multi-byte data */
|
||||
new = g_variant_get_normal_form (value);
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 3dda08e..679dd40 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -2284,24 +2284,67 @@ serialise_tree (TreeInstance *tree,
|
||||
static void
|
||||
test_byteswap (void)
|
||||
{
|
||||
- GVariantSerialised one = { 0, }, two = { 0, };
|
||||
+ GVariantSerialised one = { 0, }, two = { 0, }, three = { 0, };
|
||||
TreeInstance *tree;
|
||||
-
|
||||
+ GVariant *one_variant = NULL;
|
||||
+ GVariant *two_variant = NULL;
|
||||
+ GVariant *two_byteswapped = NULL;
|
||||
+ GVariant *three_variant = NULL;
|
||||
+ GVariant *three_byteswapped = NULL;
|
||||
+ guint8 *three_data_copy = NULL;
|
||||
+ gsize three_size_copy = 0;
|
||||
+
|
||||
+ /* Write a tree out twice, once normally and once byteswapped. */
|
||||
tree = tree_instance_new (NULL, 3);
|
||||
serialise_tree (tree, &one);
|
||||
|
||||
+ one_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (one.type_info)),
|
||||
+ one.data, one.size, FALSE, NULL, NULL);
|
||||
+
|
||||
i_am_writing_byteswapped = TRUE;
|
||||
serialise_tree (tree, &two);
|
||||
+ serialise_tree (tree, &three);
|
||||
i_am_writing_byteswapped = FALSE;
|
||||
|
||||
- g_variant_serialised_byteswap (two);
|
||||
-
|
||||
- g_assert_cmpmem (one.data, one.size, two.data, two.size);
|
||||
- g_assert_cmpuint (one.depth, ==, two.depth);
|
||||
-
|
||||
+ /* Swap the first byteswapped one back using the function we want to test. */
|
||||
+ two_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (two.type_info)),
|
||||
+ two.data, two.size, FALSE, NULL, NULL);
|
||||
+ two_byteswapped = g_variant_byteswap (two_variant);
|
||||
+
|
||||
+ /* Make the second byteswapped one non-normal (hopefully), and then byteswap
|
||||
+ * it back using the function we want to test in its non-normal mode.
|
||||
+ * This might not work because it’s not necessarily possible to make an
|
||||
+ * arbitrary random variant non-normal. Adding a single zero byte to the end
|
||||
+ * often makes something non-normal but still readable. */
|
||||
+ three_size_copy = three.size + 1;
|
||||
+ three_data_copy = g_malloc (three_size_copy);
|
||||
+ memcpy (three_data_copy, three.data, three.size);
|
||||
+ three_data_copy[three.size] = '\0';
|
||||
+
|
||||
+ three_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (three.type_info)),
|
||||
+ three_data_copy, three_size_copy, FALSE, NULL, NULL);
|
||||
+ three_byteswapped = g_variant_byteswap (three_variant);
|
||||
+
|
||||
+ /* Check they’re the same. We can always compare @one_variant and
|
||||
+ * @two_byteswapped. We can only compare @two_byteswapped and
|
||||
+ * @three_byteswapped if @two_variant and @three_variant are equal: in that
|
||||
+ * case, the corruption to @three_variant was enough to make it non-normal but
|
||||
+ * not enough to change its value. */
|
||||
+ g_assert_cmpvariant (one_variant, two_byteswapped);
|
||||
+
|
||||
+ if (g_variant_equal (two_variant, three_variant))
|
||||
+ g_assert_cmpvariant (two_byteswapped, three_byteswapped);
|
||||
+
|
||||
+ g_variant_unref (three_byteswapped);
|
||||
+ g_variant_unref (three_variant);
|
||||
+ g_variant_unref (two_byteswapped);
|
||||
+ g_variant_unref (two_variant);
|
||||
+ g_variant_unref (one_variant);
|
||||
tree_instance_free (tree);
|
||||
g_free (one.data);
|
||||
g_free (two.data);
|
||||
+ g_free (three.data);
|
||||
+ g_free (three_data_copy);
|
||||
}
|
||||
|
||||
static void
|
||||
--
|
||||
2.24.4
|
||||
|
||||
49
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32636.patch
Normal file
49
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32636.patch
Normal file
@@ -0,0 +1,49 @@
|
||||
From 21a204147b16539b3eda3143b32844c49e29f4d4 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 11:33:49 +0000
|
||||
Subject: [PATCH] gvariant: Propagate trust when getting a child of a
|
||||
serialised variant
|
||||
|
||||
If a variant is trusted, that means all its children are trusted, so
|
||||
ensure that their checked offsets are set as such.
|
||||
|
||||
This allows a lot of the offset table checks to be avoided when getting
|
||||
children from trusted serialised tuples, which speeds things up.
|
||||
|
||||
No unit test is included because this is just a performance fix. If
|
||||
there are other slownesses, or regressions, in serialised `GVariant`
|
||||
performance, the fuzzing setup will catch them like it did this one.
|
||||
|
||||
This change does reduce the time to run the oss-fuzz reproducer from 80s
|
||||
to about 0.7s on my machine.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Fixes: #2841
|
||||
oss-fuzz#54314
|
||||
|
||||
CVE: CVE-2023-32636
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/21a204147b16539b3eda3143b32844c49e29f4d4]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index 1b9d5cc..ed57c70 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -1173,8 +1173,8 @@ 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;
|
||||
+ child->contents.serialised.ordered_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.ordered_offsets_up_to;
|
||||
+ child->contents.serialised.checked_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.checked_offsets_up_to;
|
||||
|
||||
return child;
|
||||
}
|
||||
--
|
||||
2.24.4
|
||||
|
||||
154
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32643.patch
Normal file
154
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32643.patch
Normal file
@@ -0,0 +1,154 @@
|
||||
From 78da5faccb3e065116b75b3ff87ff55381da6c76 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 11:24:43 +0000
|
||||
Subject: [PATCH] gvariant: Check offset table doesn't fall outside variant
|
||||
bounds
|
||||
|
||||
When dereferencing the first entry in the offset table for a tuple,
|
||||
check that it doesn’t fall outside the bounds of the variant first.
|
||||
|
||||
This prevents an out-of-bounds read from some non-normal tuples.
|
||||
|
||||
This bug was introduced in commit 73d0aa81c2575a5c9ae77d.
|
||||
|
||||
Includes a unit test, although the test will likely only catch the
|
||||
original bug if run with asan enabled.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Fixes: #2840
|
||||
oss-fuzz#54302
|
||||
|
||||
CVE: CVE-2023-32643
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/78da5faccb3e065116b75b3ff87ff55381da6c76]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 12 ++++++--
|
||||
glib/tests/gvariant.c | 63 ++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 72 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index 5aa2cbc..4e50ed7 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -979,7 +979,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
|
||||
member_info = g_variant_type_info_member_info (value.type_info, index_);
|
||||
|
||||
- if (member_info->i + 1)
|
||||
+ if (member_info->i + 1 &&
|
||||
+ offset_size * (member_info->i + 1) <= value.size)
|
||||
member_start = gvs_read_unaligned_le (value.data + value.size -
|
||||
offset_size * (member_info->i + 1),
|
||||
offset_size);
|
||||
@@ -990,7 +991,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
member_start &= member_info->b;
|
||||
member_start |= member_info->c;
|
||||
|
||||
- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
|
||||
+ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST &&
|
||||
+ offset_size * (member_info->i + 1) <= value.size)
|
||||
member_end = value.size - offset_size * (member_info->i + 1);
|
||||
|
||||
else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
|
||||
@@ -1001,11 +1003,15 @@ gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
member_end = member_start + fixed_size;
|
||||
}
|
||||
|
||||
- else /* G_VARIANT_MEMBER_ENDING_OFFSET */
|
||||
+ else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET &&
|
||||
+ offset_size * (member_info->i + 2) <= value.size)
|
||||
member_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
offset_size * (member_info->i + 2),
|
||||
offset_size);
|
||||
|
||||
+ else /* invalid */
|
||||
+ member_end = G_MAXSIZE;
|
||||
+
|
||||
if (out_member_start != NULL)
|
||||
*out_member_start = member_start;
|
||||
if (out_member_end != NULL)
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 679dd40..2eca8be 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -5432,6 +5432,67 @@ test_normal_checking_tuple_offsets4 (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* This is a regression test that dereferencing the first element in the offset
|
||||
+ * table doesn’t dereference memory before the start of the GVariant. The first
|
||||
+ * element in the offset table gives the offset of the final member in the
|
||||
+ * tuple (the offset table is stored in reverse), and the position of this final
|
||||
+ * member is needed to check that none of the tuple members overlap with the
|
||||
+ * offset table
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2840 */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets5 (void)
|
||||
+{
|
||||
+ /* A tuple of type (sss) in normal form would have an offset table with two
|
||||
+ * entries:
|
||||
+ * - The first entry (lowest index in the table) gives the offset of the
|
||||
+ * third `s` in the tuple, as the offset table is reversed compared to the
|
||||
+ * tuple members.
|
||||
+ * - The second entry (highest index in the table) gives the offset of the
|
||||
+ * second `s` in the tuple.
|
||||
+ * - The offset of the first `s` in the tuple is always 0.
|
||||
+ *
|
||||
+ * See §2.5.4 (Structures) of the GVariant specification for details, noting
|
||||
+ * that the table is only layed out this way because all three members of the
|
||||
+ * tuple have non-fixed sizes.
|
||||
+ *
|
||||
+ * It’s not clear whether the 0xaa data of this variant is part of the strings
|
||||
+ * in the tuple, or part of the offset table. It doesn’t really matter. This
|
||||
+ * is a regression test to check that the code to validate the offset table
|
||||
+ * doesn’t unconditionally try to access the first entry in the offset table
|
||||
+ * by subtracting the table size from the end of the GVariant data.
|
||||
+ *
|
||||
+ * In this non-normal case, that would result in an address off the start of
|
||||
+ * the GVariant data, and an out-of-bounds read, because the GVariant is one
|
||||
+ * byte long, but the offset table is calculated as two bytes long (with 1B
|
||||
+ * sized entries) from the tuple’s type.
|
||||
+ */
|
||||
+ const GVariantType *data_type = G_VARIANT_TYPE ("(sss)");
|
||||
+ const guint8 data[] = { 0xaa };
|
||||
+ gsize size = sizeof (data);
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2840");
|
||||
+
|
||||
+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (variant));
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+
|
||||
+ expected = g_variant_new_parsed ("('', '', '')");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
/* Test that an otherwise-valid serialised GVariant is considered non-normal if
|
||||
* its offset table entries are too wide.
|
||||
*
|
||||
@@ -5680,6 +5741,8 @@ main (int argc, char **argv)
|
||||
test_normal_checking_tuple_offsets3);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
|
||||
test_normal_checking_tuple_offsets4);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets5",
|
||||
+ test_normal_checking_tuple_offsets5);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized",
|
||||
test_normal_checking_tuple_offsets_minimal_sized);
|
||||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||||
--
|
||||
2.24.4
|
||||
|
||||
103
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0001.patch
Normal file
103
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0001.patch
Normal file
@@ -0,0 +1,103 @@
|
||||
From 1deacdd4e8e35a5cf1417918ca4f6b0afa6409b1 Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 9 Aug 2023 10:04:49 +0000
|
||||
Subject: [PATCH] gvariant-core: Consolidate construction of
|
||||
`GVariantSerialised`
|
||||
|
||||
So I only need to change it in one place.
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/1deacdd4e8e35a5cf1417918ca4f6b0afa6409b1]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 49 ++++++++++++++++++++++----------------------
|
||||
1 file changed, 25 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index 9397573..aa0e0a0 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -349,6 +349,27 @@ 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,
|
||||
+ };
|
||||
+ return serialised;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* < private >
|
||||
* g_variant_serialise:
|
||||
* @value: a #GVariant
|
||||
@@ -991,16 +1012,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;
|
||||
|
||||
@@ -1061,12 +1074,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;
|
||||
|
||||
@@ -1179,14 +1187,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
|
||||
--
|
||||
2.24.4
|
||||
|
||||
210
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0002.patch
Normal file
210
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0002.patch
Normal file
@@ -0,0 +1,210 @@
|
||||
From 446e69f5edd72deb2196dee36bbaf8056caf6948 Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 9 Aug 2023 10:39:34 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Factor out functions for dealing with
|
||||
framing offsets
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/446e69f5edd72deb2196dee36bbaf8056caf6948]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 108 +++++++++++++++++++------------------
|
||||
1 file changed, 57 insertions(+), 51 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index 83e9d85..c7c2114 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -633,30 +633,62 @@ gvs_calculate_total_size (gsize body_size,
|
||||
return body_size + 8 * offsets;
|
||||
}
|
||||
|
||||
+struct Offsets
|
||||
+{
|
||||
+ gsize data_size;
|
||||
+
|
||||
+ guchar *array;
|
||||
+ gsize length;
|
||||
+ guint offset_size;
|
||||
+
|
||||
+ gboolean is_normal;
|
||||
+};
|
||||
+
|
||||
static gsize
|
||||
-gvs_variable_sized_array_n_children (GVariantSerialised value)
|
||||
+gvs_offsets_get_offset_n (struct Offsets *offsets,
|
||||
+ gsize n)
|
||||
+{
|
||||
+ return gvs_read_unaligned_le (
|
||||
+ offsets->array + (offsets->offset_size * n), offsets->offset_size);
|
||||
+}
|
||||
+
|
||||
+static struct Offsets
|
||||
+gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value)
|
||||
{
|
||||
+ struct Offsets out = { 0, };
|
||||
gsize offsets_array_size;
|
||||
- gsize offset_size;
|
||||
gsize last_end;
|
||||
|
||||
if (value.size == 0)
|
||||
- return 0;
|
||||
-
|
||||
- offset_size = gvs_get_offset_size (value.size);
|
||||
+ {
|
||||
+ out.is_normal = TRUE;
|
||||
+ return out;
|
||||
+ }
|
||||
|
||||
- last_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size, offset_size);
|
||||
+ out.offset_size = gvs_get_offset_size (value.size);
|
||||
+ last_end = gvs_read_unaligned_le (value.data + value.size - out.offset_size,
|
||||
+ out.offset_size);
|
||||
|
||||
if (last_end > value.size)
|
||||
- return 0;
|
||||
+ return out; /* offsets not normal */
|
||||
|
||||
offsets_array_size = value.size - last_end;
|
||||
|
||||
- if (offsets_array_size % offset_size)
|
||||
- return 0;
|
||||
+ if (offsets_array_size % out.offset_size)
|
||||
+ return out; /* offsets not normal */
|
||||
+
|
||||
+ out.data_size = last_end;
|
||||
+ out.array = value.data + last_end;
|
||||
+ out.length = offsets_array_size / out.offset_size;
|
||||
+ out.is_normal = TRUE;
|
||||
|
||||
- return offsets_array_size / offset_size;
|
||||
+ return out;
|
||||
+}
|
||||
+
|
||||
+static gsize
|
||||
+gvs_variable_sized_array_n_children (GVariantSerialised value)
|
||||
+{
|
||||
+ return gvs_variable_sized_array_get_frame_offsets (value).length;
|
||||
}
|
||||
|
||||
static GVariantSerialised
|
||||
@@ -664,8 +696,9 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
gsize index_)
|
||||
{
|
||||
GVariantSerialised child = { 0, };
|
||||
- gsize offset_size;
|
||||
- gsize last_end;
|
||||
+
|
||||
+ struct Offsets offsets = gvs_variable_sized_array_get_frame_offsets (value);
|
||||
+
|
||||
gsize start;
|
||||
gsize end;
|
||||
|
||||
@@ -673,18 +706,11 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
g_variant_type_info_ref (child.type_info);
|
||||
child.depth = value.depth + 1;
|
||||
|
||||
- offset_size = gvs_get_offset_size (value.size);
|
||||
-
|
||||
- last_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size, offset_size);
|
||||
-
|
||||
if (index_ > 0)
|
||||
{
|
||||
guint alignment;
|
||||
|
||||
- start = gvs_read_unaligned_le (value.data + last_end +
|
||||
- (offset_size * (index_ - 1)),
|
||||
- offset_size);
|
||||
+ start = gvs_offsets_get_offset_n (&offsets, index_ - 1);
|
||||
|
||||
g_variant_type_info_query (child.type_info, &alignment, NULL);
|
||||
start += (-start) & alignment;
|
||||
@@ -692,11 +718,9 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
else
|
||||
start = 0;
|
||||
|
||||
- end = gvs_read_unaligned_le (value.data + last_end +
|
||||
- (offset_size * index_),
|
||||
- offset_size);
|
||||
+ end = gvs_offsets_get_offset_n (&offsets, index_);
|
||||
|
||||
- if (start < end && end <= value.size && end <= last_end)
|
||||
+ if (start < end && end <= value.size && end <= offsets.data_size)
|
||||
{
|
||||
child.data = value.data + start;
|
||||
child.size = end - start;
|
||||
@@ -768,34 +792,16 @@ static gboolean
|
||||
gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
{
|
||||
GVariantSerialised child = { 0, };
|
||||
- gsize offsets_array_size;
|
||||
- guchar *offsets_array;
|
||||
- guint offset_size;
|
||||
guint alignment;
|
||||
- gsize last_end;
|
||||
- gsize length;
|
||||
gsize offset;
|
||||
gsize i;
|
||||
|
||||
- if (value.size == 0)
|
||||
- return TRUE;
|
||||
-
|
||||
- offset_size = gvs_get_offset_size (value.size);
|
||||
- last_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size, offset_size);
|
||||
+ struct Offsets offsets = gvs_variable_sized_array_get_frame_offsets (value);
|
||||
|
||||
- if (last_end > value.size)
|
||||
+ if (!offsets.is_normal)
|
||||
return FALSE;
|
||||
|
||||
- offsets_array_size = value.size - last_end;
|
||||
-
|
||||
- if (offsets_array_size % offset_size)
|
||||
- return FALSE;
|
||||
-
|
||||
- offsets_array = value.data + value.size - offsets_array_size;
|
||||
- length = offsets_array_size / offset_size;
|
||||
-
|
||||
- if (length == 0)
|
||||
+ if (value.size != 0 && offsets.length == 0)
|
||||
return FALSE;
|
||||
|
||||
child.type_info = g_variant_type_info_element (value.type_info);
|
||||
@@ -803,14 +809,14 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
child.depth = value.depth + 1;
|
||||
offset = 0;
|
||||
|
||||
- for (i = 0; i < length; i++)
|
||||
+ for (i = 0; i < offsets.length; i++)
|
||||
{
|
||||
gsize this_end;
|
||||
|
||||
- this_end = gvs_read_unaligned_le (offsets_array + offset_size * i,
|
||||
- offset_size);
|
||||
+ this_end = gvs_read_unaligned_le (offsets.array + offsets.offset_size * i,
|
||||
+ offsets.offset_size);
|
||||
|
||||
- if (this_end < offset || this_end > last_end)
|
||||
+ if (this_end < offset || this_end > offsets.data_size)
|
||||
return FALSE;
|
||||
|
||||
while (offset & alignment)
|
||||
@@ -832,7 +838,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
offset = this_end;
|
||||
}
|
||||
|
||||
- g_assert (offset == last_end);
|
||||
+ g_assert (offset == offsets.data_size);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
--
|
||||
2.24.4
|
||||
|
||||
417
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch
Normal file
417
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch
Normal file
@@ -0,0 +1,417 @@
|
||||
From ade71fb544391b2e33e1859645726bfee0d5eaaf Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 16 Aug 2023 03:12:21 +0000
|
||||
Subject: [PATCH] gvariant: Don't allow child elements to overlap with each
|
||||
other
|
||||
|
||||
If different elements of a variable sized array can overlap with each
|
||||
other then we can cause a `GVariant` to normalise to a much larger type.
|
||||
|
||||
This commit changes the behaviour of `GVariant` with non-normal form data. If
|
||||
an invalid frame offset is found all subsequent elements are given their
|
||||
default value.
|
||||
|
||||
When retrieving an element at index `n` we scan the frame offsets up to index
|
||||
`n` and if they are not in order we return an element with the default value
|
||||
for that type. This guarantees that elements don't overlap with each
|
||||
other. We remember the offset we've scanned up to so we don't need to
|
||||
repeat this work on subsequent accesses. We skip these checks for trusted
|
||||
data.
|
||||
|
||||
Unfortunately this makes random access of untrusted data O(n) — at least
|
||||
on first access. It doesn't affect the algorithmic complexity of accessing
|
||||
elements in order, such as when using the `GVariantIter` interface. Also:
|
||||
the cost of validation will be amortised as the `GVariant` instance is
|
||||
continued to be used.
|
||||
|
||||
I've implemented this with 4 different functions, 1 for each element size,
|
||||
rather than looping calling `gvs_read_unaligned_le` in the hope that the
|
||||
compiler will find it easy to optimise and should produce fairly tight
|
||||
code.
|
||||
|
||||
Fixes: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/ade71fb544391b2e33e1859645726bfee0d5eaaf]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 35 ++++++++++++++++
|
||||
glib/gvariant-serialiser.c | 86 ++++++++++++++++++++++++++++++++++++--
|
||||
glib/gvariant-serialiser.h | 8 ++++
|
||||
glib/tests/gvariant.c | 45 ++++++++++++++++++++
|
||||
4 files changed, 171 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index aa0e0a0..9b51e15 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -65,6 +65,7 @@ struct _GVariant
|
||||
{
|
||||
GBytes *bytes;
|
||||
gconstpointer data;
|
||||
+ gsize ordered_offsets_up_to;
|
||||
} serialised;
|
||||
|
||||
struct
|
||||
@@ -162,6 +163,24 @@ 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.
|
||||
+ *
|
||||
* .tree: Only valid when the instance is in tree form.
|
||||
*
|
||||
* Note that accesses from other threads could result in
|
||||
@@ -365,6 +384,7 @@ g_variant_to_serialised (GVariant *value)
|
||||
(gpointer) value->contents.serialised.data,
|
||||
value->size,
|
||||
value->depth,
|
||||
+ value->contents.serialised.ordered_offsets_up_to,
|
||||
};
|
||||
return serialised;
|
||||
}
|
||||
@@ -396,6 +416,7 @@ g_variant_serialise (GVariant *value,
|
||||
serialised.size = value->size;
|
||||
serialised.data = data;
|
||||
serialised.depth = value->depth;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
children = (gpointer *) value->contents.tree.children;
|
||||
n_children = value->contents.tree.n_children;
|
||||
@@ -439,6 +460,15 @@ 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;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ serialised->ordered_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.
|
||||
@@ -481,6 +511,7 @@ 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->state |= STATE_SERIALISED;
|
||||
}
|
||||
}
|
||||
@@ -561,6 +592,7 @@ 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;
|
||||
|
||||
if (!g_variant_serialised_check (serialised))
|
||||
{
|
||||
@@ -610,6 +642,8 @@ 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;
|
||||
+
|
||||
g_clear_pointer (&owned_bytes, g_bytes_unref);
|
||||
|
||||
return value;
|
||||
@@ -1108,6 +1142,7 @@ 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;
|
||||
|
||||
return child;
|
||||
}
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index c7c2114..fe0b1a4 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright © 2007, 2008 Ryan Lortie
|
||||
* Copyright © 2010 Codethink Limited
|
||||
+ * Copyright © 2020 William Manley
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -264,6 +265,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++;
|
||||
+ value.ordered_offsets_up_to = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -295,7 +297,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
|
||||
{
|
||||
if (n_children)
|
||||
{
|
||||
- GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1 };
|
||||
+ GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 };
|
||||
|
||||
gvs_filler (&child, children[0]);
|
||||
}
|
||||
@@ -317,6 +319,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++;
|
||||
+ value.ordered_offsets_up_to = 0;
|
||||
|
||||
return g_variant_serialised_is_normal (value);
|
||||
}
|
||||
@@ -358,6 +361,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
|
||||
value.data = NULL;
|
||||
|
||||
value.depth++;
|
||||
+ value.ordered_offsets_up_to = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -388,7 +392,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value,
|
||||
{
|
||||
if (n_children)
|
||||
{
|
||||
- GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1 };
|
||||
+ GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 };
|
||||
|
||||
/* write the data for the child. */
|
||||
gvs_filler (&child, children[0]);
|
||||
@@ -408,6 +412,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
|
||||
value.type_info = g_variant_type_info_element (value.type_info);
|
||||
value.size--;
|
||||
value.depth++;
|
||||
+ value.ordered_offsets_up_to = 0;
|
||||
|
||||
return g_variant_serialised_is_normal (value);
|
||||
}
|
||||
@@ -691,6 +696,32 @@ gvs_variable_sized_array_n_children (GVariantSerialised value)
|
||||
return gvs_variable_sized_array_get_frame_offsets (value).length;
|
||||
}
|
||||
|
||||
+/* Find the index of the first out-of-order element in @data, assuming that
|
||||
+ * @data is an array of elements of given @type, starting at index @start and
|
||||
+ * containing a further @len-@start elements. */
|
||||
+#define DEFINE_FIND_UNORDERED(type) \
|
||||
+ static gsize \
|
||||
+ find_unordered_##type (const guint8 *data, gsize start, gsize len) \
|
||||
+ { \
|
||||
+ gsize off; \
|
||||
+ type current, previous; \
|
||||
+ \
|
||||
+ memcpy (&previous, data + start * sizeof (current), sizeof (current)); \
|
||||
+ for (off = (start + 1) * sizeof (current); off < len * sizeof (current); off += sizeof (current)) \
|
||||
+ { \
|
||||
+ memcpy (¤t, data + off, sizeof (current)); \
|
||||
+ if (current < previous) \
|
||||
+ break; \
|
||||
+ previous = current; \
|
||||
+ } \
|
||||
+ return off / sizeof (current) - 1; \
|
||||
+ }
|
||||
+
|
||||
+DEFINE_FIND_UNORDERED (guint8);
|
||||
+DEFINE_FIND_UNORDERED (guint16);
|
||||
+DEFINE_FIND_UNORDERED (guint32);
|
||||
+DEFINE_FIND_UNORDERED (guint64);
|
||||
+
|
||||
static GVariantSerialised
|
||||
gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
gsize index_)
|
||||
@@ -706,6 +737,49 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
g_variant_type_info_ref (child.type_info);
|
||||
child.depth = value.depth + 1;
|
||||
|
||||
+ /* If the requested @index_ is beyond the set of indices whose framing offsets
|
||||
+ * have been checked, check the remaining offsets to see whether they’re
|
||||
+ * normal (in order, no overlapping array elements). */
|
||||
+ if (index_ > value.ordered_offsets_up_to)
|
||||
+ {
|
||||
+ switch (offsets.offset_size)
|
||||
+ {
|
||||
+ case 1:
|
||||
+ {
|
||||
+ value.ordered_offsets_up_to = find_unordered_guint8 (
|
||||
+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ case 2:
|
||||
+ {
|
||||
+ value.ordered_offsets_up_to = find_unordered_guint16 (
|
||||
+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ case 4:
|
||||
+ {
|
||||
+ value.ordered_offsets_up_to = find_unordered_guint32 (
|
||||
+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ case 8:
|
||||
+ {
|
||||
+ value.ordered_offsets_up_to = find_unordered_guint64 (
|
||||
+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ /* gvs_get_offset_size() only returns maximum 8 */
|
||||
+ g_assert_not_reached ();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (index_ > value.ordered_offsets_up_to)
|
||||
+ {
|
||||
+ /* Offsets are invalid somewhere, so return an empty child. */
|
||||
+ return child;
|
||||
+ }
|
||||
+
|
||||
if (index_ > 0)
|
||||
{
|
||||
guint alignment;
|
||||
@@ -840,6 +914,9 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
|
||||
g_assert (offset == offsets.data_size);
|
||||
|
||||
+ /* All offsets have now been checked. */
|
||||
+ value.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1072,7 +1149,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
const GVariantMemberInfo *member_info;
|
||||
- GVariantSerialised child;
|
||||
+ GVariantSerialised child = { 0, };
|
||||
gsize fixed_size;
|
||||
guint alignment;
|
||||
gsize end;
|
||||
@@ -1132,6 +1209,9 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
offset = end;
|
||||
}
|
||||
|
||||
+ /* All element bounds have been checked above. */
|
||||
+ value.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+
|
||||
{
|
||||
gsize fixed_size;
|
||||
guint alignment;
|
||||
diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
|
||||
index 81343e9..99d18ef 100644
|
||||
--- a/glib/gvariant-serialiser.h
|
||||
+++ b/glib/gvariant-serialiser.h
|
||||
@@ -29,6 +29,14 @@ typedef struct
|
||||
guchar *data;
|
||||
gsize size;
|
||||
gsize depth; /* same semantics as GVariant.depth */
|
||||
+ /* 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.
|
||||
+ *
|
||||
+ * This is both read and set by g_variant_serialised_get_child for arrays of
|
||||
+ * non-fixed-width types */
|
||||
+ gsize ordered_offsets_up_to;
|
||||
} GVariantSerialised;
|
||||
|
||||
/* deserialisation */
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 0e5ec8e..967e9a1 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
+ * Copyright © 2020 William Manley
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -1283,6 +1284,7 @@ random_instance_filler (GVariantSerialised *serialised,
|
||||
serialised->size = instance->size;
|
||||
|
||||
serialised->depth = 0;
|
||||
+ serialised->ordered_offsets_up_to = 0;
|
||||
|
||||
g_assert_true (serialised->type_info == instance->type_info);
|
||||
g_assert_cmpuint (serialised->size, ==, instance->size);
|
||||
@@ -5039,6 +5041,47 @@ test_normal_checking_array_offsets (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* This is a regression test that we can't have non-normal values that take up
|
||||
+ * significantly more space than the normal equivalent, by specifying the
|
||||
+ * offset table entries so that array elements overlap.
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_832242 */
|
||||
+static void
|
||||
+test_normal_checking_array_offsets2 (void)
|
||||
+{
|
||||
+ const guint8 data[] = {
|
||||
+ 'h', 'i', '\0',
|
||||
+ 0x03, 0x00, 0x03,
|
||||
+ 0x06, 0x00, 0x06,
|
||||
+ 0x09, 0x00, 0x09,
|
||||
+ 0x0c, 0x00, 0x0c,
|
||||
+ 0x0f, 0x00, 0x0f,
|
||||
+ 0x12, 0x00, 0x12,
|
||||
+ 0x15, 0x00, 0x15,
|
||||
+ };
|
||||
+ gsize size = sizeof (data);
|
||||
+ const GVariantType *aaaaaaas = G_VARIANT_TYPE ("aaaaaaas");
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ variant = g_variant_new_from_data (aaaaaaas, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 2);
|
||||
+
|
||||
+ expected = g_variant_new_parsed (
|
||||
+ "[[[[[[['hi', '', ''], [], []], [], []], [], []], [], []], [], []], [], []]");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
/* Test that a tuple with invalidly large values in its offset table is
|
||||
* normalised successfully without looping infinitely. */
|
||||
static void
|
||||
@@ -5206,6 +5249,8 @@ main (int argc, char **argv)
|
||||
test_normal_checking_tuples);
|
||||
g_test_add_func ("/gvariant/normal-checking/array-offsets",
|
||||
test_normal_checking_array_offsets);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/array-offsets2",
|
||||
+ test_normal_checking_array_offsets2);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
|
||||
test_normal_checking_tuple_offsets);
|
||||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||||
--
|
||||
2.24.4
|
||||
|
||||
113
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0004.patch
Normal file
113
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0004.patch
Normal file
@@ -0,0 +1,113 @@
|
||||
From 345cae9c1aa7bf6752039225ef4c8d8d69fa8d76 Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Fri, 11 Aug 2023 04:09:12 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Factor out code to get bounds of a tuple
|
||||
member
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/345cae9c1aa7bf6752039225ef4c8d8d69fa8d76]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 73 ++++++++++++++++++++++++--------------
|
||||
1 file changed, 46 insertions(+), 27 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index fe0b1a4..6f9b366 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -942,6 +942,51 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
* for the tuple. See the notes in gvarianttypeinfo.h.
|
||||
*/
|
||||
|
||||
+static void
|
||||
+gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
+ gsize index_,
|
||||
+ gsize offset_size,
|
||||
+ gsize *out_member_start,
|
||||
+ gsize *out_member_end)
|
||||
+{
|
||||
+ const GVariantMemberInfo *member_info;
|
||||
+ gsize member_start, member_end;
|
||||
+
|
||||
+ member_info = g_variant_type_info_member_info (value.type_info, index_);
|
||||
+
|
||||
+ if (member_info->i + 1)
|
||||
+ member_start = gvs_read_unaligned_le (value.data + value.size -
|
||||
+ offset_size * (member_info->i + 1),
|
||||
+ offset_size);
|
||||
+ else
|
||||
+ member_start = 0;
|
||||
+
|
||||
+ member_start += member_info->a;
|
||||
+ member_start &= member_info->b;
|
||||
+ member_start |= member_info->c;
|
||||
+
|
||||
+ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
|
||||
+ member_end = value.size - offset_size * (member_info->i + 1);
|
||||
+
|
||||
+ else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
|
||||
+ {
|
||||
+ gsize fixed_size;
|
||||
+
|
||||
+ g_variant_type_info_query (member_info->type_info, NULL, &fixed_size);
|
||||
+ member_end = member_start + fixed_size;
|
||||
+ }
|
||||
+
|
||||
+ else /* G_VARIANT_MEMBER_ENDING_OFFSET */
|
||||
+ member_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
+ offset_size * (member_info->i + 2),
|
||||
+ offset_size);
|
||||
+
|
||||
+ if (out_member_start != NULL)
|
||||
+ *out_member_start = member_start;
|
||||
+ if (out_member_end != NULL)
|
||||
+ *out_member_end = member_end;
|
||||
+}
|
||||
+
|
||||
static gsize
|
||||
gvs_tuple_n_children (GVariantSerialised value)
|
||||
{
|
||||
@@ -997,33 +1042,7 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
}
|
||||
}
|
||||
|
||||
- if (member_info->i + 1)
|
||||
- start = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size * (member_info->i + 1),
|
||||
- offset_size);
|
||||
- else
|
||||
- start = 0;
|
||||
-
|
||||
- start += member_info->a;
|
||||
- start &= member_info->b;
|
||||
- start |= member_info->c;
|
||||
-
|
||||
- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
|
||||
- end = value.size - offset_size * (member_info->i + 1);
|
||||
-
|
||||
- else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
|
||||
- {
|
||||
- gsize fixed_size;
|
||||
-
|
||||
- g_variant_type_info_query (child.type_info, NULL, &fixed_size);
|
||||
- end = start + fixed_size;
|
||||
- child.size = fixed_size;
|
||||
- }
|
||||
-
|
||||
- else /* G_VARIANT_MEMBER_ENDING_OFFSET */
|
||||
- end = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size * (member_info->i + 2),
|
||||
- offset_size);
|
||||
+ gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
|
||||
|
||||
/* The child should not extend into the offset table. */
|
||||
if (index_ != g_variant_type_info_n_members (value.type_info) - 1)
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
From 73d0aa81c2575a5c9ae77dcb94da919579014fc0 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Fri, 11 Aug 2023 04:13:02 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Rework child size calculation
|
||||
|
||||
This reduces a few duplicate calls to `g_variant_type_info_query()` and
|
||||
explains why they’re needed.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/73d0aa81c2575a5c9ae77dcb94da919579014fc0]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 31 +++++++++----------------------
|
||||
1 file changed, 9 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index 6f9b366..fb75923 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -1007,14 +1007,18 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
child.depth = value.depth + 1;
|
||||
offset_size = gvs_get_offset_size (value.size);
|
||||
|
||||
+ /* Ensure the size is set for fixed-sized children, or
|
||||
+ * g_variant_serialised_check() will fail, even if we return
|
||||
+ * (child.data == NULL) to indicate an error. */
|
||||
+ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
|
||||
+ g_variant_type_info_query (child.type_info, NULL, &child.size);
|
||||
+
|
||||
/* tuples are the only (potentially) fixed-sized containers, so the
|
||||
* only ones that have to deal with the possibility of having %NULL
|
||||
* data with a non-zero %size if errors occurred elsewhere.
|
||||
*/
|
||||
if G_UNLIKELY (value.data == NULL && value.size != 0)
|
||||
{
|
||||
- g_variant_type_info_query (child.type_info, NULL, &child.size);
|
||||
-
|
||||
/* this can only happen in fixed-sized tuples,
|
||||
* so the child must also be fixed sized.
|
||||
*/
|
||||
@@ -1032,29 +1036,12 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
else
|
||||
{
|
||||
if (offset_size * (member_info->i + 1) > value.size)
|
||||
- {
|
||||
- /* if the child is fixed size, return its size.
|
||||
- * if child is not fixed-sized, return size = 0.
|
||||
- */
|
||||
- g_variant_type_info_query (child.type_info, NULL, &child.size);
|
||||
-
|
||||
- return child;
|
||||
- }
|
||||
+ return child;
|
||||
}
|
||||
|
||||
- gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
|
||||
-
|
||||
/* The child should not extend into the offset table. */
|
||||
- if (index_ != g_variant_type_info_n_members (value.type_info) - 1)
|
||||
- {
|
||||
- GVariantSerialised last_child;
|
||||
- last_child = gvs_tuple_get_child (value,
|
||||
- g_variant_type_info_n_members (value.type_info) - 1);
|
||||
- last_end = last_child.data + last_child.size - value.data;
|
||||
- g_variant_type_info_unref (last_child.type_info);
|
||||
- }
|
||||
- else
|
||||
- last_end = end;
|
||||
+ gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
|
||||
+ gvs_tuple_get_member_bounds (value, g_variant_type_info_n_members (value.type_info) - 1, offset_size, NULL, &last_end);
|
||||
|
||||
if (start < end && end <= value.size && end <= last_end)
|
||||
{
|
||||
--
|
||||
2.24.4
|
||||
|
||||
396
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch
Normal file
396
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch
Normal file
@@ -0,0 +1,396 @@
|
||||
From 7cf6f5b69146d20948d42f0c476688fe17fef787 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Wed, 16 Aug 2023 12:09:06 +0000
|
||||
Subject: [PATCH] gvariant: Don't allow child elements of a tuple to overlap
|
||||
each other
|
||||
|
||||
This is similar to the earlier commit which prevents child elements of a
|
||||
variable-sized array from overlapping each other, but this time for
|
||||
tuples. It is based heavily on ideas by William Manley.
|
||||
|
||||
Tuples are slightly different from variable-sized arrays in that they
|
||||
contain a mixture of fixed and variable sized elements. All but one of
|
||||
the variable sized elements have an entry in the frame offsets table.
|
||||
This means that if we were to just check the ordering of the frame
|
||||
offsets table, the variable sized elements could still overlap
|
||||
interleaving fixed sized elements, which would be bad.
|
||||
|
||||
Therefore we have to check the elements rather than the frame offsets.
|
||||
|
||||
The logic of checking the elements up to the index currently being
|
||||
requested, and caching the result in `ordered_offsets_up_to`, means that
|
||||
the algorithmic cost implications are the same for this commit as for
|
||||
variable-sized arrays: an O(N) cost for these checks is amortised out
|
||||
over N accesses to O(1) per access.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Fixes: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/7cf6f5b69146d20948d42f0c476688fe17fef787]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 6 +-
|
||||
glib/gvariant-serialiser.c | 40 ++++++++
|
||||
glib/gvariant-serialiser.h | 7 +-
|
||||
glib/gvariant.c | 1 +
|
||||
glib/tests/gvariant.c | 181 +++++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 232 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index 9b51e15..b951cd9 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright © 2007, 2008 Ryan Lortie
|
||||
* Copyright © 2010 Codethink Limited
|
||||
+ * Copyright © 2022 Endless OS Foundation, LLC
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -179,7 +180,7 @@ struct _GVariant
|
||||
* offsets themselves.
|
||||
*
|
||||
* This field is only relevant for arrays of non
|
||||
- * fixed width types.
|
||||
+ * fixed width types and for tuples.
|
||||
*
|
||||
* .tree: Only valid when the instance is in tree form.
|
||||
*
|
||||
@@ -1117,6 +1118,9 @@ 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);
|
||||
+
|
||||
/* 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
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index fb75923..cd4a3e6 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -942,6 +942,10 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
* for the tuple. See the notes in gvarianttypeinfo.h.
|
||||
*/
|
||||
|
||||
+/* Note: This doesn’t guarantee that @out_member_end >= @out_member_start; that
|
||||
+ * condition may not hold true for invalid serialised variants. The caller is
|
||||
+ * responsible for checking the returned values and handling invalid ones
|
||||
+ * appropriately. */
|
||||
static void
|
||||
gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
gsize index_,
|
||||
@@ -1028,6 +1032,42 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
return child;
|
||||
}
|
||||
|
||||
+ /* If the requested @index_ is beyond the set of indices whose framing offsets
|
||||
+ * have been checked, check the remaining offsets to see whether they’re
|
||||
+ * normal (in order, no overlapping tuple elements).
|
||||
+ *
|
||||
+ * Unlike the checks in gvs_variable_sized_array_get_child(), we have to check
|
||||
+ * all the tuple *elements* here, not just all the framing offsets, since
|
||||
+ * tuples contain a mix of elements which use framing offsets and ones which
|
||||
+ * don’t. None of them are allowed to overlap. */
|
||||
+ if (index_ > value.ordered_offsets_up_to)
|
||||
+ {
|
||||
+ gsize i, prev_i_end = 0;
|
||||
+
|
||||
+ if (value.ordered_offsets_up_to > 0)
|
||||
+ gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
|
||||
+
|
||||
+ for (i = value.ordered_offsets_up_to; i <= index_; i++)
|
||||
+ {
|
||||
+ gsize i_start, i_end;
|
||||
+
|
||||
+ gvs_tuple_get_member_bounds (value, i, offset_size, &i_start, &i_end);
|
||||
+
|
||||
+ if (i_start > i_end || i_start < prev_i_end || i_end > value.size)
|
||||
+ break;
|
||||
+
|
||||
+ prev_i_end = i_end;
|
||||
+ }
|
||||
+
|
||||
+ value.ordered_offsets_up_to = i - 1;
|
||||
+ }
|
||||
+
|
||||
+ if (index_ > value.ordered_offsets_up_to)
|
||||
+ {
|
||||
+ /* Offsets are invalid somewhere, so return an empty child. */
|
||||
+ return child;
|
||||
+ }
|
||||
+
|
||||
if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
|
||||
{
|
||||
if (offset_size * (member_info->i + 2) > value.size)
|
||||
diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
|
||||
index 99d18ef..144aec8 100644
|
||||
--- a/glib/gvariant-serialiser.h
|
||||
+++ b/glib/gvariant-serialiser.h
|
||||
@@ -34,8 +34,11 @@ typedef struct
|
||||
* This guarantees that the bytes of element n don't overlap with any previous
|
||||
* element.
|
||||
*
|
||||
- * This is both read and set by g_variant_serialised_get_child for arrays of
|
||||
- * non-fixed-width types */
|
||||
+ * This is both read and set by g_variant_serialised_get_child() for arrays of
|
||||
+ * non-fixed-width types, and for tuples.
|
||||
+ *
|
||||
+ * Even when dealing with tuples, @ordered_offsets_up_to is an element index,
|
||||
+ * rather than an index into the frame offsets. */
|
||||
gsize ordered_offsets_up_to;
|
||||
} GVariantSerialised;
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index d6f68a9..cdb428e 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5945,6 +5945,7 @@ g_variant_byteswap (GVariant *value)
|
||||
serialised.type_info = g_variant_get_type_info (trusted);
|
||||
serialised.size = g_variant_get_size (trusted);
|
||||
serialised.data = g_malloc (serialised.size);
|
||||
+ serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */
|
||||
g_variant_store (trusted, serialised.data);
|
||||
g_variant_unref (trusted);
|
||||
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 967e9a1..a84b02e 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
* Copyright © 2020 William Manley
|
||||
+ * Copyright © 2022 Endless OS Foundation, LLC
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -1451,6 +1452,7 @@ test_maybe (void)
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised,
|
||||
random_instance_filler,
|
||||
@@ -1574,6 +1576,7 @@ test_array (void)
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) instances, n_children);
|
||||
@@ -1738,6 +1741,7 @@ test_tuple (void)
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) instances, n_children);
|
||||
@@ -1834,6 +1838,7 @@ test_variant (void)
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) &instance, 1);
|
||||
@@ -5106,6 +5111,176 @@ test_normal_checking_tuple_offsets (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* This is a regression test that we can't have non-normal values that take up
|
||||
+ * significantly more space than the normal equivalent, by specifying the
|
||||
+ * offset table entries so that tuple elements overlap.
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838503 and
|
||||
+ * https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838513 */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets2 (void)
|
||||
+{
|
||||
+ const GVariantType *data_type = G_VARIANT_TYPE ("(yyaiyyaiyy)");
|
||||
+ const guint8 data[] = {
|
||||
+ 0x12, 0x34, 0x56, 0x78, 0x01,
|
||||
+ /*
|
||||
+ ^───────────────────┘
|
||||
+
|
||||
+ ^^^^^^^^^^ 1st yy
|
||||
+ ^^^^^^^^^^ 2nd yy
|
||||
+ ^^^^^^^^^^ 3rd yy
|
||||
+ ^^^^ Framing offsets
|
||||
+ */
|
||||
+
|
||||
+ /* If this variant was encoded normally, it would be something like this:
|
||||
+ * 0x12, 0x34, pad, pad, [array bytes], 0x56, 0x78, pad, pad, [array bytes], 0x9A, 0xBC, 0xXX
|
||||
+ * ^─────────────────────────────────────────────────────┘
|
||||
+ *
|
||||
+ * ^^^^^^^^^^ 1st yy
|
||||
+ * ^^^^^^^^^^ 2nd yy
|
||||
+ * ^^^^^^^^^^ 3rd yy
|
||||
+ * ^^^^ Framing offsets
|
||||
+ */
|
||||
+ };
|
||||
+ gsize size = sizeof (data);
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
|
||||
+
|
||||
+ expected = g_variant_new_parsed (
|
||||
+ "@(yyaiyyaiyy) (0x12, 0x34, [], 0x00, 0x00, [], 0x00, 0x00)");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
+/* This is a regression test that overlapping entries in the offset table are
|
||||
+ * decoded consistently, even though they’re non-normal.
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets3 (void)
|
||||
+{
|
||||
+ /* The expected decoding of this non-normal byte stream is complex. See
|
||||
+ * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant
|
||||
+ * specification.
|
||||
+ *
|
||||
+ * The rule “Child Values Overlapping Framing Offsets” from the specification
|
||||
+ * says that the first `ay` must be decoded as `[0x01]` even though it
|
||||
+ * overlaps the first byte of the offset table. However, since commit
|
||||
+ * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow
|
||||
+ * this as it’s exploitable. So the first `ay` must be given a default value.
|
||||
+ *
|
||||
+ * The second and third `ay`s must be given default values because of rule
|
||||
+ * “End Boundary Precedes Start Boundary”.
|
||||
+ *
|
||||
+ * The `i` must be given a default value because of rule “Start or End
|
||||
+ * Boundary of a Child Falls Outside the Container”.
|
||||
+ */
|
||||
+ const GVariantType *data_type = G_VARIANT_TYPE ("(ayayiay)");
|
||||
+ const guint8 data[] = {
|
||||
+ 0x01, 0x00, 0x02,
|
||||
+ /*
|
||||
+ ^──┘
|
||||
+
|
||||
+ ^^^^^^^^^^ 1st ay, bytes 0-2 (but given a default value anyway, see above)
|
||||
+ 2nd ay, bytes 2-0
|
||||
+ i, bytes 0-4
|
||||
+ 3rd ay, bytes 4-1
|
||||
+ ^^^^^^^^^^ Framing offsets
|
||||
+ */
|
||||
+ };
|
||||
+ gsize size = sizeof (data);
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (variant));
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
|
||||
+
|
||||
+ expected = g_variant_new_parsed ("@(ayayiay) ([], [], 0, [])");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
+/* This is a regression test that overlapping entries in the offset table are
|
||||
+ * decoded consistently, even though they’re non-normal.
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets4 (void)
|
||||
+{
|
||||
+ /* The expected decoding of this non-normal byte stream is complex. See
|
||||
+ * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant
|
||||
+ * specification.
|
||||
+ *
|
||||
+ * The rule “Child Values Overlapping Framing Offsets” from the specification
|
||||
+ * says that the first `ay` must be decoded as `[0x01]` even though it
|
||||
+ * overlaps the first byte of the offset table. However, since commit
|
||||
+ * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow
|
||||
+ * this as it’s exploitable. So the first `ay` must be given a default value.
|
||||
+ *
|
||||
+ * The second `ay` must be given a default value because of rule “End Boundary
|
||||
+ * Precedes Start Boundary”.
|
||||
+ *
|
||||
+ * The third `ay` must be given a default value because its framing offsets
|
||||
+ * overlap that of the first `ay`.
|
||||
+ */
|
||||
+ const GVariantType *data_type = G_VARIANT_TYPE ("(ayayay)");
|
||||
+ const guint8 data[] = {
|
||||
+ 0x01, 0x00, 0x02,
|
||||
+ /*
|
||||
+ ^──┘
|
||||
+
|
||||
+ ^^^^^^^^^^ 1st ay, bytes 0-2 (but given a default value anyway, see above)
|
||||
+ 2nd ay, bytes 2-0
|
||||
+ 3rd ay, bytes 0-1
|
||||
+ ^^^^^^^^^^ Framing offsets
|
||||
+ */
|
||||
+ };
|
||||
+ gsize size = sizeof (data);
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (variant));
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
|
||||
+
|
||||
+ expected = g_variant_new_parsed ("@(ayayay) ([], [], [])");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
/* Test that an empty object path is normalised successfully to the base object
|
||||
* path, ‘/’. */
|
||||
static void
|
||||
@@ -5253,6 +5428,12 @@ main (int argc, char **argv)
|
||||
test_normal_checking_array_offsets2);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
|
||||
test_normal_checking_tuple_offsets);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets2",
|
||||
+ test_normal_checking_tuple_offsets2);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets3",
|
||||
+ test_normal_checking_tuple_offsets3);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
|
||||
+ test_normal_checking_tuple_offsets4);
|
||||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||||
test_normal_checking_empty_object_path);
|
||||
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
From e6490c84e84ba9f182fbd83b51ff4f9f5a0a1793 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Wed, 16 Aug 2023 03:42:47 +0000
|
||||
Subject: [PATCH] gvariant: Port g_variant_deep_copy() to count its iterations
|
||||
directly
|
||||
|
||||
This is equivalent to what `GVariantIter` does, but it means that
|
||||
`g_variant_deep_copy()` is making its own `g_variant_get_child_value()`
|
||||
calls.
|
||||
|
||||
This will be useful in an upcoming commit, where those child values will
|
||||
be inspected a little more deeply.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/e6490c84e84ba9f182fbd83b51ff4f9f5a0a1793]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant.c | 7 +++----
|
||||
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index cdb428e..fdd36be 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5799,14 +5799,13 @@ g_variant_deep_copy (GVariant *value)
|
||||
case G_VARIANT_CLASS_VARIANT:
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
- GVariantIter iter;
|
||||
- GVariant *child;
|
||||
+ gsize i, n_children;
|
||||
|
||||
g_variant_builder_init (&builder, g_variant_get_type (value));
|
||||
- g_variant_iter_init (&iter, value);
|
||||
|
||||
- while ((child = g_variant_iter_next_value (&iter)))
|
||||
+ for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++)
|
||||
{
|
||||
+ GVariant *child = g_variant_get_child_value (value, i);
|
||||
g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
|
||||
g_variant_unref (child);
|
||||
}
|
||||
--
|
||||
2.24.4
|
||||
|
||||
394
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0008.patch
Normal file
394
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0008.patch
Normal file
@@ -0,0 +1,394 @@
|
||||
From d1a293c4e29880b8d17bb826c9a426a440ca4a91 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 01:30:38 +0000
|
||||
Subject: [PATCH] gvariant: Track checked and ordered offsets independently
|
||||
|
||||
The past few commits introduced the concept of known-good offsets in the
|
||||
offset table (which is used for variable-width arrays and tuples).
|
||||
Good offsets are ones which are non-overlapping with all the previous
|
||||
offsets in the table.
|
||||
|
||||
If a bad offset is encountered when indexing into the array or tuple,
|
||||
the cached known-good offset index will not be increased. In this way,
|
||||
all child variants at and beyond the first bad offset can be returned as
|
||||
default values rather than dereferencing potentially invalid data.
|
||||
|
||||
In this case, there was no information about the fact that the indexes
|
||||
between the highest known-good index and the requested one had been
|
||||
checked already. That could lead to a pathological case where an offset
|
||||
table with an invalid first offset is repeatedly checked in full when
|
||||
trying to access higher-indexed children.
|
||||
|
||||
Avoid that by storing the index of the highest checked offset in the
|
||||
table, as well as the index of the highest good/ordered offset.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/d1a293c4e29880b8d17bb826c9a426a440ca4a91]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 28 ++++++++++++++++++++++++
|
||||
glib/gvariant-serialiser.c | 44 +++++++++++++++++++++++++++-----------
|
||||
glib/gvariant-serialiser.h | 9 ++++++++
|
||||
glib/gvariant.c | 1 +
|
||||
glib/tests/gvariant.c | 5 +++++
|
||||
5 files changed, 75 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index b951cd9..1b9d5cc 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -67,6 +67,7 @@ struct _GVariant
|
||||
GBytes *bytes;
|
||||
gconstpointer data;
|
||||
gsize ordered_offsets_up_to;
|
||||
+ gsize checked_offsets_up_to;
|
||||
} serialised;
|
||||
|
||||
struct
|
||||
@@ -182,6 +183,24 @@ struct _GVariant
|
||||
* 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
|
||||
@@ -386,6 +405,7 @@ g_variant_to_serialised (GVariant *value)
|
||||
value->size,
|
||||
value->depth,
|
||||
value->contents.serialised.ordered_offsets_up_to,
|
||||
+ value->contents.serialised.checked_offsets_up_to,
|
||||
};
|
||||
return serialised;
|
||||
}
|
||||
@@ -418,6 +438,7 @@ g_variant_serialise (GVariant *value,
|
||||
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;
|
||||
@@ -464,10 +485,12 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
|
||||
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)
|
||||
@@ -513,6 +536,7 @@ g_variant_ensure_serialised (GVariant *value)
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -594,6 +618,7 @@ g_variant_new_from_bytes (const GVariantType *type,
|
||||
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))
|
||||
{
|
||||
@@ -644,6 +669,7 @@ g_variant_new_from_bytes (const GVariantType *type,
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -1120,6 +1146,7 @@ g_variant_get_child_value (GVariant *value,
|
||||
|
||||
/* 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,
|
||||
@@ -1147,6 +1174,7 @@ g_variant_get_child_value (GVariant *value,
|
||||
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;
|
||||
}
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index cd4a3e6..0bf7243 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -120,6 +120,8 @@
|
||||
*
|
||||
* @depth has no restrictions; the depth of a top-level serialised #GVariant is
|
||||
* zero, and it increases for each level of nested child.
|
||||
+ *
|
||||
+ * @checked_offsets_up_to is always ≥ @ordered_offsets_up_to
|
||||
*/
|
||||
|
||||
/* < private >
|
||||
@@ -147,6 +149,9 @@ g_variant_serialised_check (GVariantSerialised serialised)
|
||||
!(serialised.size == 0 || serialised.data != NULL))
|
||||
return FALSE;
|
||||
|
||||
+ if (serialised.ordered_offsets_up_to > serialised.checked_offsets_up_to)
|
||||
+ return FALSE;
|
||||
+
|
||||
/* Depending on the native alignment requirements of the machine, the
|
||||
* compiler will insert either 3 or 7 padding bytes after the char.
|
||||
* This will result in the sizeof() the struct being 12 or 16.
|
||||
@@ -266,6 +271,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
|
||||
g_variant_type_info_ref (value.type_info);
|
||||
value.depth++;
|
||||
value.ordered_offsets_up_to = 0;
|
||||
+ value.checked_offsets_up_to = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -297,7 +303,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
|
||||
{
|
||||
if (n_children)
|
||||
{
|
||||
- GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 };
|
||||
+ GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0, 0 };
|
||||
|
||||
gvs_filler (&child, children[0]);
|
||||
}
|
||||
@@ -320,6 +326,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
|
||||
value.type_info = g_variant_type_info_element (value.type_info);
|
||||
value.depth++;
|
||||
value.ordered_offsets_up_to = 0;
|
||||
+ value.checked_offsets_up_to = 0;
|
||||
|
||||
return g_variant_serialised_is_normal (value);
|
||||
}
|
||||
@@ -362,6 +369,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
|
||||
|
||||
value.depth++;
|
||||
value.ordered_offsets_up_to = 0;
|
||||
+ value.checked_offsets_up_to = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -392,7 +400,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value,
|
||||
{
|
||||
if (n_children)
|
||||
{
|
||||
- GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 };
|
||||
+ GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0, 0 };
|
||||
|
||||
/* write the data for the child. */
|
||||
gvs_filler (&child, children[0]);
|
||||
@@ -413,6 +421,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
|
||||
value.size--;
|
||||
value.depth++;
|
||||
value.ordered_offsets_up_to = 0;
|
||||
+ value.checked_offsets_up_to = 0;
|
||||
|
||||
return g_variant_serialised_is_normal (value);
|
||||
}
|
||||
@@ -739,39 +748,46 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
|
||||
/* If the requested @index_ is beyond the set of indices whose framing offsets
|
||||
* have been checked, check the remaining offsets to see whether they’re
|
||||
- * normal (in order, no overlapping array elements). */
|
||||
- if (index_ > value.ordered_offsets_up_to)
|
||||
+ * normal (in order, no overlapping array elements).
|
||||
+ *
|
||||
+ * Don’t bother checking if the highest known-good offset is lower than the
|
||||
+ * highest checked offset, as that means there’s an invalid element at that
|
||||
+ * index, so there’s no need to check further. */
|
||||
+ if (index_ > value.checked_offsets_up_to &&
|
||||
+ value.ordered_offsets_up_to == value.checked_offsets_up_to)
|
||||
{
|
||||
switch (offsets.offset_size)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
value.ordered_offsets_up_to = find_unordered_guint8 (
|
||||
- offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ offsets.array, value.checked_offsets_up_to, index_ + 1);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
value.ordered_offsets_up_to = find_unordered_guint16 (
|
||||
- offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ offsets.array, value.checked_offsets_up_to, index_ + 1);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
value.ordered_offsets_up_to = find_unordered_guint32 (
|
||||
- offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ offsets.array, value.checked_offsets_up_to, index_ + 1);
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
value.ordered_offsets_up_to = find_unordered_guint64 (
|
||||
- offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ offsets.array, value.checked_offsets_up_to, index_ + 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* gvs_get_offset_size() only returns maximum 8 */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
+
|
||||
+ value.checked_offsets_up_to = index_;
|
||||
}
|
||||
|
||||
if (index_ > value.ordered_offsets_up_to)
|
||||
@@ -916,6 +932,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
|
||||
/* All offsets have now been checked. */
|
||||
value.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+ value.checked_offsets_up_to = G_MAXSIZE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1040,14 +1057,15 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
* all the tuple *elements* here, not just all the framing offsets, since
|
||||
* tuples contain a mix of elements which use framing offsets and ones which
|
||||
* don’t. None of them are allowed to overlap. */
|
||||
- if (index_ > value.ordered_offsets_up_to)
|
||||
+ if (index_ > value.checked_offsets_up_to &&
|
||||
+ value.ordered_offsets_up_to == value.checked_offsets_up_to)
|
||||
{
|
||||
gsize i, prev_i_end = 0;
|
||||
|
||||
- if (value.ordered_offsets_up_to > 0)
|
||||
- gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
|
||||
+ if (value.checked_offsets_up_to > 0)
|
||||
+ gvs_tuple_get_member_bounds (value, value.checked_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
|
||||
|
||||
- for (i = value.ordered_offsets_up_to; i <= index_; i++)
|
||||
+ for (i = value.checked_offsets_up_to; i <= index_; i++)
|
||||
{
|
||||
gsize i_start, i_end;
|
||||
|
||||
@@ -1060,6 +1078,7 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
}
|
||||
|
||||
value.ordered_offsets_up_to = i - 1;
|
||||
+ value.checked_offsets_up_to = index_;
|
||||
}
|
||||
|
||||
if (index_ > value.ordered_offsets_up_to)
|
||||
@@ -1257,6 +1276,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
|
||||
/* All element bounds have been checked above. */
|
||||
value.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+ value.checked_offsets_up_to = G_MAXSIZE;
|
||||
|
||||
{
|
||||
gsize fixed_size;
|
||||
diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
|
||||
index 144aec8..e132451 100644
|
||||
--- a/glib/gvariant-serialiser.h
|
||||
+++ b/glib/gvariant-serialiser.h
|
||||
@@ -40,6 +40,15 @@ typedef struct
|
||||
* Even when dealing with tuples, @ordered_offsets_up_to is an element index,
|
||||
* rather than an index into the frame offsets. */
|
||||
gsize ordered_offsets_up_to;
|
||||
+
|
||||
+ /* Similar to @ordered_offsets_up_to. This gives the index of the child element
|
||||
+ * whose frame offset is the highest in the offset table which has been
|
||||
+ * checked so far.
|
||||
+ *
|
||||
+ * This is always ≥ @ordered_offsets_up_to. It is always an element index.
|
||||
+ *
|
||||
+ * See documentation in gvariant-core.c for `struct GVariant` for details. */
|
||||
+ gsize checked_offsets_up_to;
|
||||
} GVariantSerialised;
|
||||
|
||||
/* deserialisation */
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index fdd36be..f910bd4 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5945,6 +5945,7 @@ g_variant_byteswap (GVariant *value)
|
||||
serialised.size = g_variant_get_size (trusted);
|
||||
serialised.data = g_malloc (serialised.size);
|
||||
serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */
|
||||
+ serialised.checked_offsets_up_to = G_MAXSIZE;
|
||||
g_variant_store (trusted, serialised.data);
|
||||
g_variant_unref (trusted);
|
||||
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index a84b02e..640f3c0 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -1286,6 +1286,7 @@ random_instance_filler (GVariantSerialised *serialised,
|
||||
|
||||
serialised->depth = 0;
|
||||
serialised->ordered_offsets_up_to = 0;
|
||||
+ serialised->checked_offsets_up_to = 0;
|
||||
|
||||
g_assert_true (serialised->type_info == instance->type_info);
|
||||
g_assert_cmpuint (serialised->size, ==, instance->size);
|
||||
@@ -1453,6 +1454,7 @@ test_maybe (void)
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised,
|
||||
random_instance_filler,
|
||||
@@ -1577,6 +1579,7 @@ test_array (void)
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) instances, n_children);
|
||||
@@ -1742,6 +1745,7 @@ test_tuple (void)
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) instances, n_children);
|
||||
@@ -1839,6 +1843,7 @@ test_variant (void)
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) &instance, 1);
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
From 298a537d5f6783e55d87e40011ee3fd3b22b72f9 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 01:39:01 +0000
|
||||
Subject: [PATCH] gvariant: Zero-initialise various GVariantSerialised objects
|
||||
|
||||
The following few commits will add a couple of new fields to
|
||||
`GVariantSerialised`, and they should be zero-filled by default.
|
||||
|
||||
Try and pre-empt that a bit by zero-filling `GVariantSerialised` by
|
||||
default in a few places.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/298a537d5f6783e55d87e40011ee3fd3b22b72f9]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant.c | 2 +-
|
||||
glib/tests/gvariant.c | 12 ++++++------
|
||||
2 files changed, 7 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index f910bd4..8ba701e 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5936,7 +5936,7 @@ g_variant_byteswap (GVariant *value)
|
||||
if (alignment)
|
||||
/* (potentially) contains multi-byte numeric data */
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
GVariant *trusted;
|
||||
GBytes *bytes;
|
||||
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 640f3c0..d640c81 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -1446,7 +1446,7 @@ test_maybe (void)
|
||||
|
||||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
GVariantSerialised child;
|
||||
|
||||
serialised.type_info = type_info;
|
||||
@@ -1572,7 +1572,7 @@ test_array (void)
|
||||
|
||||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
|
||||
serialised.type_info = array_info;
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
@@ -1738,7 +1738,7 @@ test_tuple (void)
|
||||
|
||||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
|
||||
serialised.type_info = type_info;
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
@@ -1835,7 +1835,7 @@ test_variant (void)
|
||||
|
||||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
GVariantSerialised child;
|
||||
|
||||
serialised.type_info = type_info;
|
||||
@@ -2284,7 +2284,7 @@ serialise_tree (TreeInstance *tree,
|
||||
static void
|
||||
test_byteswap (void)
|
||||
{
|
||||
- GVariantSerialised one, two;
|
||||
+ GVariantSerialised one = { 0, }, two = { 0, };
|
||||
TreeInstance *tree;
|
||||
|
||||
tree = tree_instance_new (NULL, 3);
|
||||
@@ -2358,7 +2358,7 @@ test_serialiser_children (void)
|
||||
static void
|
||||
test_fuzz (gdouble *fuzziness)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
TreeInstance *tree;
|
||||
|
||||
/* make an instance */
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -42,6 +42,20 @@ SRC_URI = "${GNOME_MIRROR}/glib/${SHRT_VER}/glib-${PV}.tar.xz \
|
||||
file://CVE-2021-28153-3.patch \
|
||||
file://CVE-2021-28153-4.patch \
|
||||
file://CVE-2021-28153-5.patch \
|
||||
file://CVE-2023-32665-0001.patch \
|
||||
file://CVE-2023-32665-0002.patch \
|
||||
file://CVE-2023-32665-0003.patch \
|
||||
file://CVE-2023-32665-0004.patch \
|
||||
file://CVE-2023-32665-0005.patch \
|
||||
file://CVE-2023-32665-0006.patch \
|
||||
file://CVE-2023-32665-0007.patch \
|
||||
file://CVE-2023-32665-0008.patch \
|
||||
file://CVE-2023-32665-0009.patch \
|
||||
file://CVE-2023-29499.patch \
|
||||
file://CVE-2023-32611-0001.patch \
|
||||
file://CVE-2023-32611-0002.patch \
|
||||
file://CVE-2023-32643.patch \
|
||||
file://CVE-2023-32636.patch \
|
||||
"
|
||||
|
||||
SRC_URI_append_class-native = " file://relocate-modules.patch"
|
||||
|
||||
Reference in New Issue
Block a user