summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch')
-rw-r--r--meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch417
1 files changed, 417 insertions, 0 deletions
diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch
new file mode 100644
index 0000000000..e361cc7aad
--- /dev/null
+++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch
@@ -0,0 +1,417 @@
1From ade71fb544391b2e33e1859645726bfee0d5eaaf Mon Sep 17 00:00:00 2001
2From: William Manley <will@stb-tester.com>
3Date: Wed, 16 Aug 2023 03:12:21 +0000
4Subject: [PATCH] gvariant: Don't allow child elements to overlap with each
5 other
6
7If different elements of a variable sized array can overlap with each
8other then we can cause a `GVariant` to normalise to a much larger type.
9
10This commit changes the behaviour of `GVariant` with non-normal form data. If
11an invalid frame offset is found all subsequent elements are given their
12default value.
13
14When retrieving an element at index `n` we scan the frame offsets up to index
15`n` and if they are not in order we return an element with the default value
16for that type. This guarantees that elements don't overlap with each
17other. We remember the offset we've scanned up to so we don't need to
18repeat this work on subsequent accesses. We skip these checks for trusted
19data.
20
21Unfortunately this makes random access of untrusted data O(n) — at least
22on first access. It doesn't affect the algorithmic complexity of accessing
23elements in order, such as when using the `GVariantIter` interface. Also:
24the cost of validation will be amortised as the `GVariant` instance is
25continued to be used.
26
27I've implemented this with 4 different functions, 1 for each element size,
28rather than looping calling `gvs_read_unaligned_le` in the hope that the
29compiler will find it easy to optimise and should produce fairly tight
30code.
31
32Fixes: #2121
33
34CVE: CVE-2023-32665
35Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/ade71fb544391b2e33e1859645726bfee0d5eaaf]
36Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
37---
38 glib/gvariant-core.c | 35 ++++++++++++++++
39 glib/gvariant-serialiser.c | 86 ++++++++++++++++++++++++++++++++++++--
40 glib/gvariant-serialiser.h | 8 ++++
41 glib/tests/gvariant.c | 45 ++++++++++++++++++++
42 4 files changed, 171 insertions(+), 3 deletions(-)
43
44diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
45index aa0e0a0..9b51e15 100644
46--- a/glib/gvariant-core.c
47+++ b/glib/gvariant-core.c
48@@ -65,6 +65,7 @@ struct _GVariant
49 {
50 GBytes *bytes;
51 gconstpointer data;
52+ gsize ordered_offsets_up_to;
53 } serialised;
54
55 struct
56@@ -162,6 +163,24 @@ struct _GVariant
57 * if .data pointed to the appropriate number of nul
58 * bytes.
59 *
60+ * .ordered_offsets_up_to: If ordered_offsets_up_to == n this means that all
61+ * the frame offsets up to and including the frame
62+ * offset determining the end of element n are in
63+ * order. This guarantees that the bytes of element
64+ * n don't overlap with any previous element.
65+ *
66+ * For trusted data this is set to G_MAXSIZE and we
67+ * don't check that the frame offsets are in order.
68+ *
69+ * Note: This doesn't imply the offsets are good in
70+ * any way apart from their ordering. In particular
71+ * offsets may be out of bounds for this value or
72+ * may imply that the data overlaps the frame
73+ * offsets themselves.
74+ *
75+ * This field is only relevant for arrays of non
76+ * fixed width types.
77+ *
78 * .tree: Only valid when the instance is in tree form.
79 *
80 * Note that accesses from other threads could result in
81@@ -365,6 +384,7 @@ g_variant_to_serialised (GVariant *value)
82 (gpointer) value->contents.serialised.data,
83 value->size,
84 value->depth,
85+ value->contents.serialised.ordered_offsets_up_to,
86 };
87 return serialised;
88 }
89@@ -396,6 +416,7 @@ g_variant_serialise (GVariant *value,
90 serialised.size = value->size;
91 serialised.data = data;
92 serialised.depth = value->depth;
93+ serialised.ordered_offsets_up_to = 0;
94
95 children = (gpointer *) value->contents.tree.children;
96 n_children = value->contents.tree.n_children;
97@@ -439,6 +460,15 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
98 g_assert (serialised->size == value->size);
99 serialised->depth = value->depth;
100
101+ if (value->state & STATE_SERIALISED)
102+ {
103+ serialised->ordered_offsets_up_to = value->contents.serialised.ordered_offsets_up_to;
104+ }
105+ else
106+ {
107+ serialised->ordered_offsets_up_to = 0;
108+ }
109+
110 if (serialised->data)
111 /* g_variant_store() is a public API, so it
112 * it will reacquire the lock if it needs to.
113@@ -481,6 +511,7 @@ g_variant_ensure_serialised (GVariant *value)
114 bytes = g_bytes_new_take (data, value->size);
115 value->contents.serialised.data = g_bytes_get_data (bytes, NULL);
116 value->contents.serialised.bytes = bytes;
117+ value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE;
118 value->state |= STATE_SERIALISED;
119 }
120 }
121@@ -561,6 +592,7 @@ g_variant_new_from_bytes (const GVariantType *type,
122 serialised.type_info = value->type_info;
123 serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size);
124 serialised.depth = 0;
125+ serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
126
127 if (!g_variant_serialised_check (serialised))
128 {
129@@ -610,6 +642,8 @@ g_variant_new_from_bytes (const GVariantType *type,
130 value->contents.serialised.data = g_bytes_get_data (bytes, &value->size);
131 }
132
133+ value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
134+
135 g_clear_pointer (&owned_bytes, g_bytes_unref);
136
137 return value;
138@@ -1108,6 +1142,7 @@ g_variant_get_child_value (GVariant *value,
139 child->contents.serialised.bytes =
140 g_bytes_ref (value->contents.serialised.bytes);
141 child->contents.serialised.data = s_child.data;
142+ child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
143
144 return child;
145 }
146diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
147index c7c2114..fe0b1a4 100644
148--- a/glib/gvariant-serialiser.c
149+++ b/glib/gvariant-serialiser.c
150@@ -1,6 +1,7 @@
151 /*
152 * Copyright © 2007, 2008 Ryan Lortie
153 * Copyright © 2010 Codethink Limited
154+ * Copyright © 2020 William Manley
155 *
156 * This library is free software; you can redistribute it and/or
157 * modify it under the terms of the GNU Lesser General Public
158@@ -264,6 +265,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
159 value.type_info = g_variant_type_info_element (value.type_info);
160 g_variant_type_info_ref (value.type_info);
161 value.depth++;
162+ value.ordered_offsets_up_to = 0;
163
164 return value;
165 }
166@@ -295,7 +297,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
167 {
168 if (n_children)
169 {
170- GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1 };
171+ GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 };
172
173 gvs_filler (&child, children[0]);
174 }
175@@ -317,6 +319,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
176 /* proper element size: "Just". recurse to the child. */
177 value.type_info = g_variant_type_info_element (value.type_info);
178 value.depth++;
179+ value.ordered_offsets_up_to = 0;
180
181 return g_variant_serialised_is_normal (value);
182 }
183@@ -358,6 +361,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
184 value.data = NULL;
185
186 value.depth++;
187+ value.ordered_offsets_up_to = 0;
188
189 return value;
190 }
191@@ -388,7 +392,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value,
192 {
193 if (n_children)
194 {
195- GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1 };
196+ GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 };
197
198 /* write the data for the child. */
199 gvs_filler (&child, children[0]);
200@@ -408,6 +412,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
201 value.type_info = g_variant_type_info_element (value.type_info);
202 value.size--;
203 value.depth++;
204+ value.ordered_offsets_up_to = 0;
205
206 return g_variant_serialised_is_normal (value);
207 }
208@@ -691,6 +696,32 @@ gvs_variable_sized_array_n_children (GVariantSerialised value)
209 return gvs_variable_sized_array_get_frame_offsets (value).length;
210 }
211
212+/* Find the index of the first out-of-order element in @data, assuming that
213+ * @data is an array of elements of given @type, starting at index @start and
214+ * containing a further @len-@start elements. */
215+#define DEFINE_FIND_UNORDERED(type) \
216+ static gsize \
217+ find_unordered_##type (const guint8 *data, gsize start, gsize len) \
218+ { \
219+ gsize off; \
220+ type current, previous; \
221+ \
222+ memcpy (&previous, data + start * sizeof (current), sizeof (current)); \
223+ for (off = (start + 1) * sizeof (current); off < len * sizeof (current); off += sizeof (current)) \
224+ { \
225+ memcpy (&current, data + off, sizeof (current)); \
226+ if (current < previous) \
227+ break; \
228+ previous = current; \
229+ } \
230+ return off / sizeof (current) - 1; \
231+ }
232+
233+DEFINE_FIND_UNORDERED (guint8);
234+DEFINE_FIND_UNORDERED (guint16);
235+DEFINE_FIND_UNORDERED (guint32);
236+DEFINE_FIND_UNORDERED (guint64);
237+
238 static GVariantSerialised
239 gvs_variable_sized_array_get_child (GVariantSerialised value,
240 gsize index_)
241@@ -706,6 +737,49 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
242 g_variant_type_info_ref (child.type_info);
243 child.depth = value.depth + 1;
244
245+ /* If the requested @index_ is beyond the set of indices whose framing offsets
246+ * have been checked, check the remaining offsets to see whether they’re
247+ * normal (in order, no overlapping array elements). */
248+ if (index_ > value.ordered_offsets_up_to)
249+ {
250+ switch (offsets.offset_size)
251+ {
252+ case 1:
253+ {
254+ value.ordered_offsets_up_to = find_unordered_guint8 (
255+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
256+ break;
257+ }
258+ case 2:
259+ {
260+ value.ordered_offsets_up_to = find_unordered_guint16 (
261+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
262+ break;
263+ }
264+ case 4:
265+ {
266+ value.ordered_offsets_up_to = find_unordered_guint32 (
267+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
268+ break;
269+ }
270+ case 8:
271+ {
272+ value.ordered_offsets_up_to = find_unordered_guint64 (
273+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
274+ break;
275+ }
276+ default:
277+ /* gvs_get_offset_size() only returns maximum 8 */
278+ g_assert_not_reached ();
279+ }
280+ }
281+
282+ if (index_ > value.ordered_offsets_up_to)
283+ {
284+ /* Offsets are invalid somewhere, so return an empty child. */
285+ return child;
286+ }
287+
288 if (index_ > 0)
289 {
290 guint alignment;
291@@ -840,6 +914,9 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
292
293 g_assert (offset == offsets.data_size);
294
295+ /* All offsets have now been checked. */
296+ value.ordered_offsets_up_to = G_MAXSIZE;
297+
298 return TRUE;
299 }
300
301@@ -1072,7 +1149,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
302 for (i = 0; i < length; i++)
303 {
304 const GVariantMemberInfo *member_info;
305- GVariantSerialised child;
306+ GVariantSerialised child = { 0, };
307 gsize fixed_size;
308 guint alignment;
309 gsize end;
310@@ -1132,6 +1209,9 @@ gvs_tuple_is_normal (GVariantSerialised value)
311 offset = end;
312 }
313
314+ /* All element bounds have been checked above. */
315+ value.ordered_offsets_up_to = G_MAXSIZE;
316+
317 {
318 gsize fixed_size;
319 guint alignment;
320diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
321index 81343e9..99d18ef 100644
322--- a/glib/gvariant-serialiser.h
323+++ b/glib/gvariant-serialiser.h
324@@ -29,6 +29,14 @@ typedef struct
325 guchar *data;
326 gsize size;
327 gsize depth; /* same semantics as GVariant.depth */
328+ /* If ordered_offsets_up_to == n this means that all the frame offsets up to and
329+ * including the frame offset determining the end of element n are in order.
330+ * This guarantees that the bytes of element n don't overlap with any previous
331+ * element.
332+ *
333+ * This is both read and set by g_variant_serialised_get_child for arrays of
334+ * non-fixed-width types */
335+ gsize ordered_offsets_up_to;
336 } GVariantSerialised;
337
338 /* deserialisation */
339diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
340index 0e5ec8e..967e9a1 100644
341--- a/glib/tests/gvariant.c
342+++ b/glib/tests/gvariant.c
343@@ -1,5 +1,6 @@
344 /*
345 * Copyright © 2010 Codethink Limited
346+ * Copyright © 2020 William Manley
347 *
348 * This library is free software; you can redistribute it and/or
349 * modify it under the terms of the GNU Lesser General Public
350@@ -1283,6 +1284,7 @@ random_instance_filler (GVariantSerialised *serialised,
351 serialised->size = instance->size;
352
353 serialised->depth = 0;
354+ serialised->ordered_offsets_up_to = 0;
355
356 g_assert_true (serialised->type_info == instance->type_info);
357 g_assert_cmpuint (serialised->size, ==, instance->size);
358@@ -5039,6 +5041,47 @@ test_normal_checking_array_offsets (void)
359 g_variant_unref (variant);
360 }
361
362+/* This is a regression test that we can't have non-normal values that take up
363+ * significantly more space than the normal equivalent, by specifying the
364+ * offset table entries so that array elements overlap.
365+ *
366+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_832242 */
367+static void
368+test_normal_checking_array_offsets2 (void)
369+{
370+ const guint8 data[] = {
371+ 'h', 'i', '\0',
372+ 0x03, 0x00, 0x03,
373+ 0x06, 0x00, 0x06,
374+ 0x09, 0x00, 0x09,
375+ 0x0c, 0x00, 0x0c,
376+ 0x0f, 0x00, 0x0f,
377+ 0x12, 0x00, 0x12,
378+ 0x15, 0x00, 0x15,
379+ };
380+ gsize size = sizeof (data);
381+ const GVariantType *aaaaaaas = G_VARIANT_TYPE ("aaaaaaas");
382+ GVariant *variant = NULL;
383+ GVariant *normal_variant = NULL;
384+ GVariant *expected = NULL;
385+
386+ variant = g_variant_new_from_data (aaaaaaas, data, size, FALSE, NULL, NULL);
387+ g_assert_nonnull (variant);
388+
389+ normal_variant = g_variant_get_normal_form (variant);
390+ g_assert_nonnull (normal_variant);
391+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 2);
392+
393+ expected = g_variant_new_parsed (
394+ "[[[[[[['hi', '', ''], [], []], [], []], [], []], [], []], [], []], [], []]");
395+ g_assert_cmpvariant (expected, variant);
396+ g_assert_cmpvariant (expected, normal_variant);
397+
398+ g_variant_unref (expected);
399+ g_variant_unref (normal_variant);
400+ g_variant_unref (variant);
401+}
402+
403 /* Test that a tuple with invalidly large values in its offset table is
404 * normalised successfully without looping infinitely. */
405 static void
406@@ -5206,6 +5249,8 @@ main (int argc, char **argv)
407 test_normal_checking_tuples);
408 g_test_add_func ("/gvariant/normal-checking/array-offsets",
409 test_normal_checking_array_offsets);
410+ g_test_add_func ("/gvariant/normal-checking/array-offsets2",
411+ test_normal_checking_array_offsets2);
412 g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
413 test_normal_checking_tuple_offsets);
414 g_test_add_func ("/gvariant/normal-checking/empty-object-path",
415--
4162.24.4
417