summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch')
-rw-r--r--meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch396
1 files changed, 396 insertions, 0 deletions
diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch
new file mode 100644
index 0000000000..8558a7911f
--- /dev/null
+++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch
@@ -0,0 +1,396 @@
1From 7cf6f5b69146d20948d42f0c476688fe17fef787 Mon Sep 17 00:00:00 2001
2From: Philip Withnall <pwithnall@endlessos.org>
3Date: Wed, 16 Aug 2023 12:09:06 +0000
4Subject: [PATCH] gvariant: Don't allow child elements of a tuple to overlap
5 each other
6
7This is similar to the earlier commit which prevents child elements of a
8variable-sized array from overlapping each other, but this time for
9tuples. It is based heavily on ideas by William Manley.
10
11Tuples are slightly different from variable-sized arrays in that they
12contain a mixture of fixed and variable sized elements. All but one of
13the variable sized elements have an entry in the frame offsets table.
14This means that if we were to just check the ordering of the frame
15offsets table, the variable sized elements could still overlap
16interleaving fixed sized elements, which would be bad.
17
18Therefore we have to check the elements rather than the frame offsets.
19
20The logic of checking the elements up to the index currently being
21requested, and caching the result in `ordered_offsets_up_to`, means that
22the algorithmic cost implications are the same for this commit as for
23variable-sized arrays: an O(N) cost for these checks is amortised out
24over N accesses to O(1) per access.
25
26Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
27
28Fixes: #2121
29
30CVE: CVE-2023-32665
31Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/7cf6f5b69146d20948d42f0c476688fe17fef787]
32Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
33---
34 glib/gvariant-core.c | 6 +-
35 glib/gvariant-serialiser.c | 40 ++++++++
36 glib/gvariant-serialiser.h | 7 +-
37 glib/gvariant.c | 1 +
38 glib/tests/gvariant.c | 181 +++++++++++++++++++++++++++++++++++++
39 5 files changed, 232 insertions(+), 3 deletions(-)
40
41diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
42index 9b51e15..b951cd9 100644
43--- a/glib/gvariant-core.c
44+++ b/glib/gvariant-core.c
45@@ -1,6 +1,7 @@
46 /*
47 * Copyright © 2007, 2008 Ryan Lortie
48 * Copyright © 2010 Codethink Limited
49+ * Copyright © 2022 Endless OS Foundation, LLC
50 *
51 * This library is free software; you can redistribute it and/or
52 * modify it under the terms of the GNU Lesser General Public
53@@ -179,7 +180,7 @@ struct _GVariant
54 * offsets themselves.
55 *
56 * This field is only relevant for arrays of non
57- * fixed width types.
58+ * fixed width types and for tuples.
59 *
60 * .tree: Only valid when the instance is in tree form.
61 *
62@@ -1117,6 +1118,9 @@ g_variant_get_child_value (GVariant *value,
63 */
64 s_child = g_variant_serialised_get_child (serialised, index_);
65
66+ /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
67+ value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
68+
69 /* Check whether this would cause nesting too deep. If so, return a fake
70 * child. The only situation we expect this to happen in is with a variant,
71 * as all other deeply-nested types have a static type, and hence should
72diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
73index fb75923..cd4a3e6 100644
74--- a/glib/gvariant-serialiser.c
75+++ b/glib/gvariant-serialiser.c
76@@ -942,6 +942,10 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
77 * for the tuple. See the notes in gvarianttypeinfo.h.
78 */
79
80+/* Note: This doesn’t guarantee that @out_member_end >= @out_member_start; that
81+ * condition may not hold true for invalid serialised variants. The caller is
82+ * responsible for checking the returned values and handling invalid ones
83+ * appropriately. */
84 static void
85 gvs_tuple_get_member_bounds (GVariantSerialised value,
86 gsize index_,
87@@ -1028,6 +1032,42 @@ gvs_tuple_get_child (GVariantSerialised value,
88 return child;
89 }
90
91+ /* If the requested @index_ is beyond the set of indices whose framing offsets
92+ * have been checked, check the remaining offsets to see whether they’re
93+ * normal (in order, no overlapping tuple elements).
94+ *
95+ * Unlike the checks in gvs_variable_sized_array_get_child(), we have to check
96+ * all the tuple *elements* here, not just all the framing offsets, since
97+ * tuples contain a mix of elements which use framing offsets and ones which
98+ * don’t. None of them are allowed to overlap. */
99+ if (index_ > value.ordered_offsets_up_to)
100+ {
101+ gsize i, prev_i_end = 0;
102+
103+ if (value.ordered_offsets_up_to > 0)
104+ gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
105+
106+ for (i = value.ordered_offsets_up_to; i <= index_; i++)
107+ {
108+ gsize i_start, i_end;
109+
110+ gvs_tuple_get_member_bounds (value, i, offset_size, &i_start, &i_end);
111+
112+ if (i_start > i_end || i_start < prev_i_end || i_end > value.size)
113+ break;
114+
115+ prev_i_end = i_end;
116+ }
117+
118+ value.ordered_offsets_up_to = i - 1;
119+ }
120+
121+ if (index_ > value.ordered_offsets_up_to)
122+ {
123+ /* Offsets are invalid somewhere, so return an empty child. */
124+ return child;
125+ }
126+
127 if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
128 {
129 if (offset_size * (member_info->i + 2) > value.size)
130diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
131index 99d18ef..144aec8 100644
132--- a/glib/gvariant-serialiser.h
133+++ b/glib/gvariant-serialiser.h
134@@ -34,8 +34,11 @@ typedef struct
135 * This guarantees that the bytes of element n don't overlap with any previous
136 * element.
137 *
138- * This is both read and set by g_variant_serialised_get_child for arrays of
139- * non-fixed-width types */
140+ * This is both read and set by g_variant_serialised_get_child() for arrays of
141+ * non-fixed-width types, and for tuples.
142+ *
143+ * Even when dealing with tuples, @ordered_offsets_up_to is an element index,
144+ * rather than an index into the frame offsets. */
145 gsize ordered_offsets_up_to;
146 } GVariantSerialised;
147
148diff --git a/glib/gvariant.c b/glib/gvariant.c
149index d6f68a9..cdb428e 100644
150--- a/glib/gvariant.c
151+++ b/glib/gvariant.c
152@@ -5945,6 +5945,7 @@ g_variant_byteswap (GVariant *value)
153 serialised.type_info = g_variant_get_type_info (trusted);
154 serialised.size = g_variant_get_size (trusted);
155 serialised.data = g_malloc (serialised.size);
156+ serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */
157 g_variant_store (trusted, serialised.data);
158 g_variant_unref (trusted);
159
160diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
161index 967e9a1..a84b02e 100644
162--- a/glib/tests/gvariant.c
163+++ b/glib/tests/gvariant.c
164@@ -1,6 +1,7 @@
165 /*
166 * Copyright © 2010 Codethink Limited
167 * Copyright © 2020 William Manley
168+ * Copyright © 2022 Endless OS Foundation, LLC
169 *
170 * This library is free software; you can redistribute it and/or
171 * modify it under the terms of the GNU Lesser General Public
172@@ -1451,6 +1452,7 @@ test_maybe (void)
173 serialised.data = flavoured_malloc (needed_size, flavour);
174 serialised.size = needed_size;
175 serialised.depth = 0;
176+ serialised.ordered_offsets_up_to = 0;
177
178 g_variant_serialiser_serialise (serialised,
179 random_instance_filler,
180@@ -1574,6 +1576,7 @@ test_array (void)
181 serialised.data = flavoured_malloc (needed_size, flavour);
182 serialised.size = needed_size;
183 serialised.depth = 0;
184+ serialised.ordered_offsets_up_to = 0;
185
186 g_variant_serialiser_serialise (serialised, random_instance_filler,
187 (gpointer *) instances, n_children);
188@@ -1738,6 +1741,7 @@ test_tuple (void)
189 serialised.data = flavoured_malloc (needed_size, flavour);
190 serialised.size = needed_size;
191 serialised.depth = 0;
192+ serialised.ordered_offsets_up_to = 0;
193
194 g_variant_serialiser_serialise (serialised, random_instance_filler,
195 (gpointer *) instances, n_children);
196@@ -1834,6 +1838,7 @@ test_variant (void)
197 serialised.data = flavoured_malloc (needed_size, flavour);
198 serialised.size = needed_size;
199 serialised.depth = 0;
200+ serialised.ordered_offsets_up_to = 0;
201
202 g_variant_serialiser_serialise (serialised, random_instance_filler,
203 (gpointer *) &instance, 1);
204@@ -5106,6 +5111,176 @@ test_normal_checking_tuple_offsets (void)
205 g_variant_unref (variant);
206 }
207
208+/* This is a regression test that we can't have non-normal values that take up
209+ * significantly more space than the normal equivalent, by specifying the
210+ * offset table entries so that tuple elements overlap.
211+ *
212+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838503 and
213+ * https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838513 */
214+static void
215+test_normal_checking_tuple_offsets2 (void)
216+{
217+ const GVariantType *data_type = G_VARIANT_TYPE ("(yyaiyyaiyy)");
218+ const guint8 data[] = {
219+ 0x12, 0x34, 0x56, 0x78, 0x01,
220+ /*
221+ ^───────────────────┘
222+
223+ ^^^^^^^^^^ 1st yy
224+ ^^^^^^^^^^ 2nd yy
225+ ^^^^^^^^^^ 3rd yy
226+ ^^^^ Framing offsets
227+ */
228+
229+ /* If this variant was encoded normally, it would be something like this:
230+ * 0x12, 0x34, pad, pad, [array bytes], 0x56, 0x78, pad, pad, [array bytes], 0x9A, 0xBC, 0xXX
231+ * ^─────────────────────────────────────────────────────┘
232+ *
233+ * ^^^^^^^^^^ 1st yy
234+ * ^^^^^^^^^^ 2nd yy
235+ * ^^^^^^^^^^ 3rd yy
236+ * ^^^^ Framing offsets
237+ */
238+ };
239+ gsize size = sizeof (data);
240+ GVariant *variant = NULL;
241+ GVariant *normal_variant = NULL;
242+ GVariant *expected = NULL;
243+
244+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
245+ g_assert_nonnull (variant);
246+
247+ normal_variant = g_variant_get_normal_form (variant);
248+ g_assert_nonnull (normal_variant);
249+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
250+
251+ expected = g_variant_new_parsed (
252+ "@(yyaiyyaiyy) (0x12, 0x34, [], 0x00, 0x00, [], 0x00, 0x00)");
253+ g_assert_cmpvariant (expected, variant);
254+ g_assert_cmpvariant (expected, normal_variant);
255+
256+ g_variant_unref (expected);
257+ g_variant_unref (normal_variant);
258+ g_variant_unref (variant);
259+}
260+
261+/* This is a regression test that overlapping entries in the offset table are
262+ * decoded consistently, even though they’re non-normal.
263+ *
264+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */
265+static void
266+test_normal_checking_tuple_offsets3 (void)
267+{
268+ /* The expected decoding of this non-normal byte stream is complex. See
269+ * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant
270+ * specification.
271+ *
272+ * The rule “Child Values Overlapping Framing Offsets” from the specification
273+ * says that the first `ay` must be decoded as `[0x01]` even though it
274+ * overlaps the first byte of the offset table. However, since commit
275+ * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow
276+ * this as it’s exploitable. So the first `ay` must be given a default value.
277+ *
278+ * The second and third `ay`s must be given default values because of rule
279+ * “End Boundary Precedes Start Boundary”.
280+ *
281+ * The `i` must be given a default value because of rule “Start or End
282+ * Boundary of a Child Falls Outside the Container”.
283+ */
284+ const GVariantType *data_type = G_VARIANT_TYPE ("(ayayiay)");
285+ const guint8 data[] = {
286+ 0x01, 0x00, 0x02,
287+ /*
288+ ^──┘
289+
290+ ^^^^^^^^^^ 1st ay, bytes 0-2 (but given a default value anyway, see above)
291+ 2nd ay, bytes 2-0
292+ i, bytes 0-4
293+ 3rd ay, bytes 4-1
294+ ^^^^^^^^^^ Framing offsets
295+ */
296+ };
297+ gsize size = sizeof (data);
298+ GVariant *variant = NULL;
299+ GVariant *normal_variant = NULL;
300+ GVariant *expected = NULL;
301+
302+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
303+ g_assert_nonnull (variant);
304+
305+ g_assert_false (g_variant_is_normal_form (variant));
306+
307+ normal_variant = g_variant_get_normal_form (variant);
308+ g_assert_nonnull (normal_variant);
309+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
310+
311+ expected = g_variant_new_parsed ("@(ayayiay) ([], [], 0, [])");
312+ g_assert_cmpvariant (expected, variant);
313+ g_assert_cmpvariant (expected, normal_variant);
314+
315+ g_variant_unref (expected);
316+ g_variant_unref (normal_variant);
317+ g_variant_unref (variant);
318+}
319+
320+/* This is a regression test that overlapping entries in the offset table are
321+ * decoded consistently, even though they’re non-normal.
322+ *
323+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */
324+static void
325+test_normal_checking_tuple_offsets4 (void)
326+{
327+ /* The expected decoding of this non-normal byte stream is complex. See
328+ * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant
329+ * specification.
330+ *
331+ * The rule “Child Values Overlapping Framing Offsets” from the specification
332+ * says that the first `ay` must be decoded as `[0x01]` even though it
333+ * overlaps the first byte of the offset table. However, since commit
334+ * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow
335+ * this as it’s exploitable. So the first `ay` must be given a default value.
336+ *
337+ * The second `ay` must be given a default value because of rule “End Boundary
338+ * Precedes Start Boundary”.
339+ *
340+ * The third `ay` must be given a default value because its framing offsets
341+ * overlap that of the first `ay`.
342+ */
343+ const GVariantType *data_type = G_VARIANT_TYPE ("(ayayay)");
344+ const guint8 data[] = {
345+ 0x01, 0x00, 0x02,
346+ /*
347+ ^──┘
348+
349+ ^^^^^^^^^^ 1st ay, bytes 0-2 (but given a default value anyway, see above)
350+ 2nd ay, bytes 2-0
351+ 3rd ay, bytes 0-1
352+ ^^^^^^^^^^ Framing offsets
353+ */
354+ };
355+ gsize size = sizeof (data);
356+ GVariant *variant = NULL;
357+ GVariant *normal_variant = NULL;
358+ GVariant *expected = NULL;
359+
360+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
361+ g_assert_nonnull (variant);
362+
363+ g_assert_false (g_variant_is_normal_form (variant));
364+
365+ normal_variant = g_variant_get_normal_form (variant);
366+ g_assert_nonnull (normal_variant);
367+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
368+
369+ expected = g_variant_new_parsed ("@(ayayay) ([], [], [])");
370+ g_assert_cmpvariant (expected, variant);
371+ g_assert_cmpvariant (expected, normal_variant);
372+
373+ g_variant_unref (expected);
374+ g_variant_unref (normal_variant);
375+ g_variant_unref (variant);
376+}
377+
378 /* Test that an empty object path is normalised successfully to the base object
379 * path, ‘/’. */
380 static void
381@@ -5253,6 +5428,12 @@ main (int argc, char **argv)
382 test_normal_checking_array_offsets2);
383 g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
384 test_normal_checking_tuple_offsets);
385+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets2",
386+ test_normal_checking_tuple_offsets2);
387+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets3",
388+ test_normal_checking_tuple_offsets3);
389+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
390+ test_normal_checking_tuple_offsets4);
391 g_test_add_func ("/gvariant/normal-checking/empty-object-path",
392 test_normal_checking_empty_object_path);
393
394--
3952.24.4
396