diff options
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.patch | 417 |
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 @@ | |||
1 | From ade71fb544391b2e33e1859645726bfee0d5eaaf Mon Sep 17 00:00:00 2001 | ||
2 | From: William Manley <will@stb-tester.com> | ||
3 | Date: Wed, 16 Aug 2023 03:12:21 +0000 | ||
4 | Subject: [PATCH] gvariant: Don't allow child elements to overlap with each | ||
5 | other | ||
6 | |||
7 | If different elements of a variable sized array can overlap with each | ||
8 | other then we can cause a `GVariant` to normalise to a much larger type. | ||
9 | |||
10 | This commit changes the behaviour of `GVariant` with non-normal form data. If | ||
11 | an invalid frame offset is found all subsequent elements are given their | ||
12 | default value. | ||
13 | |||
14 | When 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 | ||
16 | for that type. This guarantees that elements don't overlap with each | ||
17 | other. We remember the offset we've scanned up to so we don't need to | ||
18 | repeat this work on subsequent accesses. We skip these checks for trusted | ||
19 | data. | ||
20 | |||
21 | Unfortunately this makes random access of untrusted data O(n) — at least | ||
22 | on first access. It doesn't affect the algorithmic complexity of accessing | ||
23 | elements in order, such as when using the `GVariantIter` interface. Also: | ||
24 | the cost of validation will be amortised as the `GVariant` instance is | ||
25 | continued to be used. | ||
26 | |||
27 | I've implemented this with 4 different functions, 1 for each element size, | ||
28 | rather than looping calling `gvs_read_unaligned_le` in the hope that the | ||
29 | compiler will find it easy to optimise and should produce fairly tight | ||
30 | code. | ||
31 | |||
32 | Fixes: #2121 | ||
33 | |||
34 | CVE: CVE-2023-32665 | ||
35 | Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/ade71fb544391b2e33e1859645726bfee0d5eaaf] | ||
36 | Signed-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 | |||
44 | diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c | ||
45 | index 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 | } | ||
146 | diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c | ||
147 | index 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 (¤t, 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; | ||
320 | diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h | ||
321 | index 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 */ | ||
339 | diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c | ||
340 | index 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 | -- | ||
416 | 2.24.4 | ||
417 | |||