diff options
Diffstat (limited to 'meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch')
-rw-r--r-- | meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch new file mode 100644 index 0000000000..ce90586290 --- /dev/null +++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch | |||
@@ -0,0 +1,290 @@ | |||
1 | From 5f4485c4ff57fdefb1661531788def7ca5a47328 Mon Sep 17 00:00:00 2001 | ||
2 | From: Philip Withnall <pwithnall@endlessos.org> | ||
3 | Date: Thu, 17 Aug 2023 04:19:44 +0000 | ||
4 | Subject: [PATCH] gvariant-serialiser: Check offset table entry size is minimal | ||
5 | |||
6 | The entries in an offset table (which is used for variable sized arrays | ||
7 | and tuples containing variable sized members) are sized so that they can | ||
8 | address every byte in the overall variant. | ||
9 | |||
10 | The specification requires that for a variant to be in normal form, its | ||
11 | offset table entries must be the minimum width such that they can | ||
12 | address every byte in the variant. | ||
13 | |||
14 | That minimality requirement was not checked in | ||
15 | `g_variant_is_normal_form()`, leading to two different byte arrays being | ||
16 | interpreted as the normal form of a given variant tree. That kind of | ||
17 | confusion could potentially be exploited, and is certainly a bug. | ||
18 | |||
19 | Fix it by adding the necessary checks on offset table entry width, and | ||
20 | unit tests. | ||
21 | |||
22 | Spotted by William Manley. | ||
23 | |||
24 | Signed-off-by: Philip Withnall <pwithnall@endlessos.org> | ||
25 | |||
26 | Fixes: #2794 | ||
27 | |||
28 | CVE: CVE-2023-29499 | ||
29 | Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/5f4485c4ff57fdefb1661531788def7ca5a47328] | ||
30 | Signed-off-by: Siddharth Doshi <sdoshi@mvista.com> | ||
31 | --- | ||
32 | glib/gvariant-serialiser.c | 19 +++- | ||
33 | glib/tests/gvariant.c | 176 +++++++++++++++++++++++++++++++++++++ | ||
34 | 2 files changed, 194 insertions(+), 1 deletion(-) | ||
35 | |||
36 | diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c | ||
37 | index 0bf7243..5aa2cbc 100644 | ||
38 | --- a/glib/gvariant-serialiser.c | ||
39 | +++ b/glib/gvariant-serialiser.c | ||
40 | @@ -694,6 +694,10 @@ gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value) | ||
41 | out.data_size = last_end; | ||
42 | out.array = value.data + last_end; | ||
43 | out.length = offsets_array_size / out.offset_size; | ||
44 | + | ||
45 | + if (out.length > 0 && gvs_calculate_total_size (last_end, out.length) != value.size) | ||
46 | + return out; /* offset size not minimal */ | ||
47 | + | ||
48 | out.is_normal = TRUE; | ||
49 | |||
50 | return out; | ||
51 | @@ -1201,6 +1205,7 @@ gvs_tuple_is_normal (GVariantSerialised value) | ||
52 | gsize length; | ||
53 | gsize offset; | ||
54 | gsize i; | ||
55 | + gsize offset_table_size; | ||
56 | |||
57 | /* as per the comment in gvs_tuple_get_child() */ | ||
58 | if G_UNLIKELY (value.data == NULL && value.size != 0) | ||
59 | @@ -1305,7 +1310,19 @@ gvs_tuple_is_normal (GVariantSerialised value) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | - return offset_ptr == offset; | ||
64 | + /* @offset_ptr has been counting backwards from the end of the variant, to | ||
65 | + * find the beginning of the offset table. @offset has been counting forwards | ||
66 | + * from the beginning of the variant to find the end of the data. They should | ||
67 | + * have met in the middle. */ | ||
68 | + if (offset_ptr != offset) | ||
69 | + return FALSE; | ||
70 | + | ||
71 | + offset_table_size = value.size - offset_ptr; | ||
72 | + if (value.size > 0 && | ||
73 | + gvs_calculate_total_size (offset, offset_table_size / offset_size) != value.size) | ||
74 | + return FALSE; /* offset size not minimal */ | ||
75 | + | ||
76 | + return TRUE; | ||
77 | } | ||
78 | |||
79 | /* Variants {{{2 | ||
80 | diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c | ||
81 | index d640c81..4ce0e4f 100644 | ||
82 | --- a/glib/tests/gvariant.c | ||
83 | +++ b/glib/tests/gvariant.c | ||
84 | @@ -5092,6 +5092,86 @@ test_normal_checking_array_offsets2 (void) | ||
85 | g_variant_unref (variant); | ||
86 | } | ||
87 | |||
88 | +/* Test that an otherwise-valid serialised GVariant is considered non-normal if | ||
89 | + * its offset table entries are too wide. | ||
90 | + * | ||
91 | + * See §2.3.6 (Framing Offsets) of the GVariant specification. */ | ||
92 | +static void | ||
93 | +test_normal_checking_array_offsets_minimal_sized (void) | ||
94 | +{ | ||
95 | + GVariantBuilder builder; | ||
96 | + gsize i; | ||
97 | + GVariant *aay_constructed = NULL; | ||
98 | + const guint8 *data = NULL; | ||
99 | + guint8 *data_owned = NULL; | ||
100 | + GVariant *aay_deserialised = NULL; | ||
101 | + GVariant *aay_normalised = NULL; | ||
102 | + | ||
103 | + /* Construct an array of type aay, consisting of 128 elements which are each | ||
104 | + * an empty array, i.e. `[[] * 128]`. This is chosen because the inner | ||
105 | + * elements are variable sized (making the outer array variable sized, so it | ||
106 | + * must have an offset table), but they are also zero-sized when serialised. | ||
107 | + * So the serialised representation of @aay_constructed consists entirely of | ||
108 | + * its offset table, which is entirely zeroes. | ||
109 | + * | ||
110 | + * The array is chosen to be 128 elements long because that means offset | ||
111 | + * table entries which are 1 byte long. If the elements in the array were | ||
112 | + * non-zero-sized (to the extent that the overall array is ≥256 bytes long), | ||
113 | + * the offset table entries would end up being 2 bytes long. */ | ||
114 | + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay")); | ||
115 | + | ||
116 | + for (i = 0; i < 128; i++) | ||
117 | + g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0)); | ||
118 | + | ||
119 | + aay_constructed = g_variant_builder_end (&builder); | ||
120 | + | ||
121 | + /* Verify that the constructed array is in normal form, and its serialised | ||
122 | + * form is `b'\0' * 128`. */ | ||
123 | + g_assert_true (g_variant_is_normal_form (aay_constructed)); | ||
124 | + g_assert_cmpuint (g_variant_n_children (aay_constructed), ==, 128); | ||
125 | + g_assert_cmpuint (g_variant_get_size (aay_constructed), ==, 128); | ||
126 | + | ||
127 | + data = g_variant_get_data (aay_constructed); | ||
128 | + for (i = 0; i < g_variant_get_size (aay_constructed); i++) | ||
129 | + g_assert_cmpuint (data[i], ==, 0); | ||
130 | + | ||
131 | + /* Construct a serialised `aay` GVariant which is `b'\0' * 256`. This has to | ||
132 | + * be a non-normal form of `[[] * 128]`, with 2-byte-long offset table | ||
133 | + * entries, because each offset table entry has to be able to reference all of | ||
134 | + * the byte boundaries in the container. All the entries in the offset table | ||
135 | + * are zero, so all the elements of the array are zero-sized. */ | ||
136 | + data = data_owned = g_malloc0 (256); | ||
137 | + aay_deserialised = g_variant_new_from_data (G_VARIANT_TYPE ("aay"), | ||
138 | + data, | ||
139 | + 256, | ||
140 | + FALSE, | ||
141 | + g_free, | ||
142 | + g_steal_pointer (&data_owned)); | ||
143 | + | ||
144 | + g_assert_false (g_variant_is_normal_form (aay_deserialised)); | ||
145 | + g_assert_cmpuint (g_variant_n_children (aay_deserialised), ==, 128); | ||
146 | + g_assert_cmpuint (g_variant_get_size (aay_deserialised), ==, 256); | ||
147 | + | ||
148 | + data = g_variant_get_data (aay_deserialised); | ||
149 | + for (i = 0; i < g_variant_get_size (aay_deserialised); i++) | ||
150 | + g_assert_cmpuint (data[i], ==, 0); | ||
151 | + | ||
152 | + /* Get its normal form. That should change the serialised size. */ | ||
153 | + aay_normalised = g_variant_get_normal_form (aay_deserialised); | ||
154 | + | ||
155 | + g_assert_true (g_variant_is_normal_form (aay_normalised)); | ||
156 | + g_assert_cmpuint (g_variant_n_children (aay_normalised), ==, 128); | ||
157 | + g_assert_cmpuint (g_variant_get_size (aay_normalised), ==, 128); | ||
158 | + | ||
159 | + data = g_variant_get_data (aay_normalised); | ||
160 | + for (i = 0; i < g_variant_get_size (aay_normalised); i++) | ||
161 | + g_assert_cmpuint (data[i], ==, 0); | ||
162 | + | ||
163 | + g_variant_unref (aay_normalised); | ||
164 | + g_variant_unref (aay_deserialised); | ||
165 | + g_variant_unref (aay_constructed); | ||
166 | +} | ||
167 | + | ||
168 | /* Test that a tuple with invalidly large values in its offset table is | ||
169 | * normalised successfully without looping infinitely. */ | ||
170 | static void | ||
171 | @@ -5286,6 +5366,98 @@ test_normal_checking_tuple_offsets4 (void) | ||
172 | g_variant_unref (variant); | ||
173 | } | ||
174 | |||
175 | +/* Test that an otherwise-valid serialised GVariant is considered non-normal if | ||
176 | + * its offset table entries are too wide. | ||
177 | + * | ||
178 | + * See §2.3.6 (Framing Offsets) of the GVariant specification. */ | ||
179 | +static void | ||
180 | +test_normal_checking_tuple_offsets_minimal_sized (void) | ||
181 | +{ | ||
182 | + GString *type_string = NULL; | ||
183 | + GVariantBuilder builder; | ||
184 | + gsize i; | ||
185 | + GVariant *ray_constructed = NULL; | ||
186 | + const guint8 *data = NULL; | ||
187 | + guint8 *data_owned = NULL; | ||
188 | + GVariant *ray_deserialised = NULL; | ||
189 | + GVariant *ray_normalised = NULL; | ||
190 | + | ||
191 | + /* Construct a tuple of type (ay…ay), consisting of 129 members which are each | ||
192 | + * an empty array, i.e. `([] * 129)`. This is chosen because the inner | ||
193 | + * members are variable sized, so the outer tuple must have an offset table, | ||
194 | + * but they are also zero-sized when serialised. So the serialised | ||
195 | + * representation of @ray_constructed consists entirely of its offset table, | ||
196 | + * which is entirely zeroes. | ||
197 | + * | ||
198 | + * The tuple is chosen to be 129 members long because that means it has 128 | ||
199 | + * offset table entries which are 1 byte long each. If the members in the | ||
200 | + * tuple were non-zero-sized (to the extent that the overall tuple is ≥256 | ||
201 | + * bytes long), the offset table entries would end up being 2 bytes long. | ||
202 | + * | ||
203 | + * 129 members are used unlike 128 array elements in | ||
204 | + * test_normal_checking_array_offsets_minimal_sized(), because the last member | ||
205 | + * in a tuple never needs an offset table entry. */ | ||
206 | + type_string = g_string_new (""); | ||
207 | + g_string_append_c (type_string, '('); | ||
208 | + for (i = 0; i < 129; i++) | ||
209 | + g_string_append (type_string, "ay"); | ||
210 | + g_string_append_c (type_string, ')'); | ||
211 | + | ||
212 | + g_variant_builder_init (&builder, G_VARIANT_TYPE (type_string->str)); | ||
213 | + | ||
214 | + for (i = 0; i < 129; i++) | ||
215 | + g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0)); | ||
216 | + | ||
217 | + ray_constructed = g_variant_builder_end (&builder); | ||
218 | + | ||
219 | + /* Verify that the constructed tuple is in normal form, and its serialised | ||
220 | + * form is `b'\0' * 128`. */ | ||
221 | + g_assert_true (g_variant_is_normal_form (ray_constructed)); | ||
222 | + g_assert_cmpuint (g_variant_n_children (ray_constructed), ==, 129); | ||
223 | + g_assert_cmpuint (g_variant_get_size (ray_constructed), ==, 128); | ||
224 | + | ||
225 | + data = g_variant_get_data (ray_constructed); | ||
226 | + for (i = 0; i < g_variant_get_size (ray_constructed); i++) | ||
227 | + g_assert_cmpuint (data[i], ==, 0); | ||
228 | + | ||
229 | + /* Construct a serialised `(ay…ay)` GVariant which is `b'\0' * 256`. This has | ||
230 | + * to be a non-normal form of `([] * 129)`, with 2-byte-long offset table | ||
231 | + * entries, because each offset table entry has to be able to reference all of | ||
232 | + * the byte boundaries in the container. All the entries in the offset table | ||
233 | + * are zero, so all the members of the tuple are zero-sized. */ | ||
234 | + data = data_owned = g_malloc0 (256); | ||
235 | + ray_deserialised = g_variant_new_from_data (G_VARIANT_TYPE (type_string->str), | ||
236 | + data, | ||
237 | + 256, | ||
238 | + FALSE, | ||
239 | + g_free, | ||
240 | + g_steal_pointer (&data_owned)); | ||
241 | + | ||
242 | + g_assert_false (g_variant_is_normal_form (ray_deserialised)); | ||
243 | + g_assert_cmpuint (g_variant_n_children (ray_deserialised), ==, 129); | ||
244 | + g_assert_cmpuint (g_variant_get_size (ray_deserialised), ==, 256); | ||
245 | + | ||
246 | + data = g_variant_get_data (ray_deserialised); | ||
247 | + for (i = 0; i < g_variant_get_size (ray_deserialised); i++) | ||
248 | + g_assert_cmpuint (data[i], ==, 0); | ||
249 | + | ||
250 | + /* Get its normal form. That should change the serialised size. */ | ||
251 | + ray_normalised = g_variant_get_normal_form (ray_deserialised); | ||
252 | + | ||
253 | + g_assert_true (g_variant_is_normal_form (ray_normalised)); | ||
254 | + g_assert_cmpuint (g_variant_n_children (ray_normalised), ==, 129); | ||
255 | + g_assert_cmpuint (g_variant_get_size (ray_normalised), ==, 128); | ||
256 | + | ||
257 | + data = g_variant_get_data (ray_normalised); | ||
258 | + for (i = 0; i < g_variant_get_size (ray_normalised); i++) | ||
259 | + g_assert_cmpuint (data[i], ==, 0); | ||
260 | + | ||
261 | + g_variant_unref (ray_normalised); | ||
262 | + g_variant_unref (ray_deserialised); | ||
263 | + g_variant_unref (ray_constructed); | ||
264 | + g_string_free (type_string, TRUE); | ||
265 | +} | ||
266 | + | ||
267 | /* Test that an empty object path is normalised successfully to the base object | ||
268 | * path, ‘/’. */ | ||
269 | static void | ||
270 | @@ -5431,6 +5603,8 @@ main (int argc, char **argv) | ||
271 | test_normal_checking_array_offsets); | ||
272 | g_test_add_func ("/gvariant/normal-checking/array-offsets2", | ||
273 | test_normal_checking_array_offsets2); | ||
274 | + g_test_add_func ("/gvariant/normal-checking/array-offsets/minimal-sized", | ||
275 | + test_normal_checking_array_offsets_minimal_sized); | ||
276 | g_test_add_func ("/gvariant/normal-checking/tuple-offsets", | ||
277 | test_normal_checking_tuple_offsets); | ||
278 | g_test_add_func ("/gvariant/normal-checking/tuple-offsets2", | ||
279 | @@ -5439,6 +5613,8 @@ main (int argc, char **argv) | ||
280 | test_normal_checking_tuple_offsets3); | ||
281 | g_test_add_func ("/gvariant/normal-checking/tuple-offsets4", | ||
282 | test_normal_checking_tuple_offsets4); | ||
283 | + g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized", | ||
284 | + test_normal_checking_tuple_offsets_minimal_sized); | ||
285 | g_test_add_func ("/gvariant/normal-checking/empty-object-path", | ||
286 | test_normal_checking_empty_object_path); | ||
287 | |||
288 | -- | ||
289 | 2.24.4 | ||
290 | |||