1b5975d6bSopenharmony_ciFrom 78da5faccb3e065116b75b3ff87ff55381da6c76 Mon Sep 17 00:00:00 2001
2b5975d6bSopenharmony_ciFrom: Philip Withnall <pwithnall@endlessos.org>
3b5975d6bSopenharmony_ciDate: Thu, 15 Dec 2022 13:00:39 +0000
4b5975d6bSopenharmony_ciSubject: [PATCH 1/2] =?UTF-8?q?gvariant:=20Check=20offset=20table=20doesn?=
5b5975d6bSopenharmony_ci =?UTF-8?q?=E2=80=99t=20fall=20outside=20variant=20bounds?=
6b5975d6bSopenharmony_ciMIME-Version: 1.0
7b5975d6bSopenharmony_ciContent-Type: text/plain; charset=UTF-8
8b5975d6bSopenharmony_ciContent-Transfer-Encoding: 8bit
9b5975d6bSopenharmony_ci
10b5975d6bSopenharmony_ciWhen dereferencing the first entry in the offset table for a tuple,
11b5975d6bSopenharmony_cicheck that it doesn’t fall outside the bounds of the variant first.
12b5975d6bSopenharmony_ci
13b5975d6bSopenharmony_ciThis prevents an out-of-bounds read from some non-normal tuples.
14b5975d6bSopenharmony_ci
15b5975d6bSopenharmony_ciThis bug was introduced in commit 73d0aa81c2575a5c9ae77d.
16b5975d6bSopenharmony_ci
17b5975d6bSopenharmony_ciIncludes a unit test, although the test will likely only catch the
18b5975d6bSopenharmony_cioriginal bug if run with asan enabled.
19b5975d6bSopenharmony_ci
20b5975d6bSopenharmony_ciSigned-off-by: Philip Withnall <pwithnall@endlessos.org>
21b5975d6bSopenharmony_ci
22b5975d6bSopenharmony_ciFixes: #2840
23b5975d6bSopenharmony_cioss-fuzz#54302
24b5975d6bSopenharmony_ci---
25b5975d6bSopenharmony_ci glib/gvariant-serialiser.c | 12 ++++++--
26b5975d6bSopenharmony_ci glib/tests/gvariant.c      | 63 ++++++++++++++++++++++++++++++++++++++
27b5975d6bSopenharmony_ci 2 files changed, 72 insertions(+), 3 deletions(-)
28b5975d6bSopenharmony_ci
29b5975d6bSopenharmony_cidiff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
30b5975d6bSopenharmony_ciindex f443c2eb85..4e4a73ad17 100644
31b5975d6bSopenharmony_ci--- a/glib/gvariant-serialiser.c
32b5975d6bSopenharmony_ci+++ b/glib/gvariant-serialiser.c
33b5975d6bSopenharmony_ci@@ -984,7 +984,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
34b5975d6bSopenharmony_ci 
35b5975d6bSopenharmony_ci   member_info = g_variant_type_info_member_info (value.type_info, index_);
36b5975d6bSopenharmony_ci 
37b5975d6bSopenharmony_ci-  if (member_info->i + 1)
38b5975d6bSopenharmony_ci+  if (member_info->i + 1 &&
39b5975d6bSopenharmony_ci+      offset_size * (member_info->i + 1) <= value.size)
40b5975d6bSopenharmony_ci     member_start = gvs_read_unaligned_le (value.data + value.size -
41b5975d6bSopenharmony_ci                                           offset_size * (member_info->i + 1),
42b5975d6bSopenharmony_ci                                           offset_size);
43b5975d6bSopenharmony_ci@@ -995,7 +996,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
44b5975d6bSopenharmony_ci   member_start &= member_info->b;
45b5975d6bSopenharmony_ci   member_start |= member_info->c;
46b5975d6bSopenharmony_ci 
47b5975d6bSopenharmony_ci-  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
48b5975d6bSopenharmony_ci+  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST &&
49b5975d6bSopenharmony_ci+      offset_size * (member_info->i + 1) <= value.size)
50b5975d6bSopenharmony_ci     member_end = value.size - offset_size * (member_info->i + 1);
51b5975d6bSopenharmony_ci 
52b5975d6bSopenharmony_ci   else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
53b5975d6bSopenharmony_ci@@ -1006,11 +1008,15 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
54b5975d6bSopenharmony_ci       member_end = member_start + fixed_size;
55b5975d6bSopenharmony_ci     }
56b5975d6bSopenharmony_ci 
57b5975d6bSopenharmony_ci-  else /* G_VARIANT_MEMBER_ENDING_OFFSET */
58b5975d6bSopenharmony_ci+  else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET &&
59b5975d6bSopenharmony_ci+           offset_size * (member_info->i + 2) <= value.size)
60b5975d6bSopenharmony_ci     member_end = gvs_read_unaligned_le (value.data + value.size -
61b5975d6bSopenharmony_ci                                         offset_size * (member_info->i + 2),
62b5975d6bSopenharmony_ci                                         offset_size);
63b5975d6bSopenharmony_ci 
64b5975d6bSopenharmony_ci+  else  /* invalid */
65b5975d6bSopenharmony_ci+    member_end = G_MAXSIZE;
66b5975d6bSopenharmony_ci+
67b5975d6bSopenharmony_ci   if (out_member_start != NULL)
68b5975d6bSopenharmony_ci     *out_member_start = member_start;
69b5975d6bSopenharmony_ci   if (out_member_end != NULL)
70b5975d6bSopenharmony_cidiff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
71b5975d6bSopenharmony_ciindex b360888e4d..98c51a1d75 100644
72b5975d6bSopenharmony_ci--- a/glib/tests/gvariant.c
73b5975d6bSopenharmony_ci+++ b/glib/tests/gvariant.c
74b5975d6bSopenharmony_ci@@ -5576,6 +5576,67 @@ test_normal_checking_tuple_offsets4 (void)
75b5975d6bSopenharmony_ci   g_variant_unref (variant);
76b5975d6bSopenharmony_ci }
77b5975d6bSopenharmony_ci 
78b5975d6bSopenharmony_ci+/* This is a regression test that dereferencing the first element in the offset
79b5975d6bSopenharmony_ci+ * table doesn’t dereference memory before the start of the GVariant. The first
80b5975d6bSopenharmony_ci+ * element in the offset table gives the offset of the final member in the
81b5975d6bSopenharmony_ci+ * tuple (the offset table is stored in reverse), and the position of this final
82b5975d6bSopenharmony_ci+ * member is needed to check that none of the tuple members overlap with the
83b5975d6bSopenharmony_ci+ * offset table
84b5975d6bSopenharmony_ci+ *
85b5975d6bSopenharmony_ci+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2840 */
86b5975d6bSopenharmony_ci+static void
87b5975d6bSopenharmony_ci+test_normal_checking_tuple_offsets5 (void)
88b5975d6bSopenharmony_ci+{
89b5975d6bSopenharmony_ci+  /* A tuple of type (sss) in normal form would have an offset table with two
90b5975d6bSopenharmony_ci+   * entries:
91b5975d6bSopenharmony_ci+   *  - The first entry (lowest index in the table) gives the offset of the
92b5975d6bSopenharmony_ci+   *    third `s` in the tuple, as the offset table is reversed compared to the
93b5975d6bSopenharmony_ci+   *    tuple members.
94b5975d6bSopenharmony_ci+   *  - The second entry (highest index in the table) gives the offset of the
95b5975d6bSopenharmony_ci+   *    second `s` in the tuple.
96b5975d6bSopenharmony_ci+   *  - The offset of the first `s` in the tuple is always 0.
97b5975d6bSopenharmony_ci+   *
98b5975d6bSopenharmony_ci+   * See §2.5.4 (Structures) of the GVariant specification for details, noting
99b5975d6bSopenharmony_ci+   * that the table is only layed out this way because all three members of the
100b5975d6bSopenharmony_ci+   * tuple have non-fixed sizes.
101b5975d6bSopenharmony_ci+   *
102b5975d6bSopenharmony_ci+   * It’s not clear whether the 0xaa data of this variant is part of the strings
103b5975d6bSopenharmony_ci+   * in the tuple, or part of the offset table. It doesn’t really matter. This
104b5975d6bSopenharmony_ci+   * is a regression test to check that the code to validate the offset table
105b5975d6bSopenharmony_ci+   * doesn’t unconditionally try to access the first entry in the offset table
106b5975d6bSopenharmony_ci+   * by subtracting the table size from the end of the GVariant data.
107b5975d6bSopenharmony_ci+   *
108b5975d6bSopenharmony_ci+   * In this non-normal case, that would result in an address off the start of
109b5975d6bSopenharmony_ci+   * the GVariant data, and an out-of-bounds read, because the GVariant is one
110b5975d6bSopenharmony_ci+   * byte long, but the offset table is calculated as two bytes long (with 1B
111b5975d6bSopenharmony_ci+   * sized entries) from the tuple’s type.
112b5975d6bSopenharmony_ci+   */
113b5975d6bSopenharmony_ci+  const GVariantType *data_type = G_VARIANT_TYPE ("(sss)");
114b5975d6bSopenharmony_ci+  const guint8 data[] = { 0xaa };
115b5975d6bSopenharmony_ci+  gsize size = sizeof (data);
116b5975d6bSopenharmony_ci+  GVariant *variant = NULL;
117b5975d6bSopenharmony_ci+  GVariant *normal_variant = NULL;
118b5975d6bSopenharmony_ci+  GVariant *expected = NULL;
119b5975d6bSopenharmony_ci+
120b5975d6bSopenharmony_ci+  g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2840");
121b5975d6bSopenharmony_ci+
122b5975d6bSopenharmony_ci+  variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
123b5975d6bSopenharmony_ci+  g_assert_nonnull (variant);
124b5975d6bSopenharmony_ci+
125b5975d6bSopenharmony_ci+  g_assert_false (g_variant_is_normal_form (variant));
126b5975d6bSopenharmony_ci+
127b5975d6bSopenharmony_ci+  normal_variant = g_variant_get_normal_form (variant);
128b5975d6bSopenharmony_ci+  g_assert_nonnull (normal_variant);
129b5975d6bSopenharmony_ci+
130b5975d6bSopenharmony_ci+  expected = g_variant_new_parsed ("('', '', '')");
131b5975d6bSopenharmony_ci+  g_assert_cmpvariant (expected, variant);
132b5975d6bSopenharmony_ci+  g_assert_cmpvariant (expected, normal_variant);
133b5975d6bSopenharmony_ci+
134b5975d6bSopenharmony_ci+  g_variant_unref (expected);
135b5975d6bSopenharmony_ci+  g_variant_unref (normal_variant);
136b5975d6bSopenharmony_ci+  g_variant_unref (variant);
137b5975d6bSopenharmony_ci+}
138b5975d6bSopenharmony_ci+
139b5975d6bSopenharmony_ci /* Test that an otherwise-valid serialised GVariant is considered non-normal if
140b5975d6bSopenharmony_ci  * its offset table entries are too wide.
141b5975d6bSopenharmony_ci  *
142b5975d6bSopenharmony_ci@@ -5827,6 +5888,8 @@ main (int argc, char **argv)
143b5975d6bSopenharmony_ci                    test_normal_checking_tuple_offsets3);
144b5975d6bSopenharmony_ci   g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
145b5975d6bSopenharmony_ci                    test_normal_checking_tuple_offsets4);
146b5975d6bSopenharmony_ci+  g_test_add_func ("/gvariant/normal-checking/tuple-offsets5",
147b5975d6bSopenharmony_ci+                   test_normal_checking_tuple_offsets5);
148b5975d6bSopenharmony_ci   g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized",
149b5975d6bSopenharmony_ci                    test_normal_checking_tuple_offsets_minimal_sized);
150b5975d6bSopenharmony_ci   g_test_add_func ("/gvariant/normal-checking/empty-object-path",
151b5975d6bSopenharmony_ci-- 
152b5975d6bSopenharmony_ciGitLab
153b5975d6bSopenharmony_ci
154b5975d6bSopenharmony_ci
155b5975d6bSopenharmony_ciFrom 21a204147b16539b3eda3143b32844c49e29f4d4 Mon Sep 17 00:00:00 2001
156b5975d6bSopenharmony_ciFrom: Philip Withnall <pwithnall@endlessos.org>
157b5975d6bSopenharmony_ciDate: Thu, 15 Dec 2022 16:49:28 +0000
158b5975d6bSopenharmony_ciSubject: [PATCH 2/2] gvariant: Propagate trust when getting a child of a
159b5975d6bSopenharmony_ci serialised variant
160b5975d6bSopenharmony_ci
161b5975d6bSopenharmony_ciIf a variant is trusted, that means all its children are trusted, so
162b5975d6bSopenharmony_ciensure that their checked offsets are set as such.
163b5975d6bSopenharmony_ci
164b5975d6bSopenharmony_ciThis allows a lot of the offset table checks to be avoided when getting
165b5975d6bSopenharmony_cichildren from trusted serialised tuples, which speeds things up.
166b5975d6bSopenharmony_ci
167b5975d6bSopenharmony_ciNo unit test is included because this is just a performance fix. If
168b5975d6bSopenharmony_cithere are other slownesses, or regressions, in serialised `GVariant`
169b5975d6bSopenharmony_ciperformance, the fuzzing setup will catch them like it did this one.
170b5975d6bSopenharmony_ci
171b5975d6bSopenharmony_ciThis change does reduce the time to run the oss-fuzz reproducer from 80s
172b5975d6bSopenharmony_cito about 0.7s on my machine.
173b5975d6bSopenharmony_ci
174b5975d6bSopenharmony_ciSigned-off-by: Philip Withnall <pwithnall@endlessos.org>
175b5975d6bSopenharmony_ci
176b5975d6bSopenharmony_ciFixes: #2841
177b5975d6bSopenharmony_cioss-fuzz#54314
178b5975d6bSopenharmony_ci---
179b5975d6bSopenharmony_ci glib/gvariant-core.c | 4 ++--
180b5975d6bSopenharmony_ci 1 file changed, 2 insertions(+), 2 deletions(-)
181b5975d6bSopenharmony_ci
182b5975d6bSopenharmony_cidiff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
183b5975d6bSopenharmony_ciindex f441c4757e..4778022829 100644
184b5975d6bSopenharmony_ci--- a/glib/gvariant-core.c
185b5975d6bSopenharmony_ci+++ b/glib/gvariant-core.c
186b5975d6bSopenharmony_ci@@ -1198,8 +1198,8 @@ g_variant_get_child_value (GVariant *value,
187b5975d6bSopenharmony_ci     child->contents.serialised.bytes =
188b5975d6bSopenharmony_ci       g_bytes_ref (value->contents.serialised.bytes);
189b5975d6bSopenharmony_ci     child->contents.serialised.data = s_child.data;
190b5975d6bSopenharmony_ci-    child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
191b5975d6bSopenharmony_ci-    child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to;
192b5975d6bSopenharmony_ci+    child->contents.serialised.ordered_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.ordered_offsets_up_to;
193b5975d6bSopenharmony_ci+    child->contents.serialised.checked_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.checked_offsets_up_to;
194b5975d6bSopenharmony_ci 
195b5975d6bSopenharmony_ci     return child;
196b5975d6bSopenharmony_ci   }
197b5975d6bSopenharmony_ci-- 
198b5975d6bSopenharmony_ciGitLab
199b5975d6bSopenharmony_ci
200