diff options
4 files changed, 673 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..65174efa6d --- /dev/null +++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch | |||
@@ -0,0 +1,291 @@ | |||
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 | |||
30 | Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/glib/-/commit/5f4485c4ff57fdefb1661531788def7ca5a47328] | ||
31 | |||
32 | Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> | ||
33 | --- | ||
34 | glib/gvariant-serialiser.c | 19 +++- | ||
35 | glib/tests/gvariant.c | 176 +++++++++++++++++++++++++++++++++++++ | ||
36 | 2 files changed, 194 insertions(+), 1 deletion(-) | ||
37 | |||
38 | diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c | ||
39 | index 9c7f12a..3d6e7b8 100644 | ||
40 | --- a/glib/gvariant-serialiser.c | ||
41 | +++ b/glib/gvariant-serialiser.c | ||
42 | @@ -694,6 +694,10 @@ gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value) | ||
43 | out.data_size = last_end; | ||
44 | out.array = value.data + last_end; | ||
45 | out.length = offsets_array_size / out.offset_size; | ||
46 | + | ||
47 | + if (out.length > 0 && gvs_calculate_total_size (last_end, out.length) != value.size) | ||
48 | + return out; /* offset size not minimal */ | ||
49 | + | ||
50 | out.is_normal = TRUE; | ||
51 | |||
52 | return out; | ||
53 | @@ -1201,6 +1205,7 @@ gvs_tuple_is_normal (GVariantSerialised value) | ||
54 | gsize length; | ||
55 | gsize offset; | ||
56 | gsize i; | ||
57 | + gsize offset_table_size; | ||
58 | |||
59 | /* as per the comment in gvs_tuple_get_child() */ | ||
60 | if G_UNLIKELY (value.data == NULL && value.size != 0) | ||
61 | @@ -1305,7 +1310,19 @@ gvs_tuple_is_normal (GVariantSerialised value) | ||
62 | } | ||
63 | } | ||
64 | |||
65 | - return offset_ptr == offset; | ||
66 | + /* @offset_ptr has been counting backwards from the end of the variant, to | ||
67 | + * find the beginning of the offset table. @offset has been counting forwards | ||
68 | + * from the beginning of the variant to find the end of the data. They should | ||
69 | + * have met in the middle. */ | ||
70 | + if (offset_ptr != offset) | ||
71 | + return FALSE; | ||
72 | + | ||
73 | + offset_table_size = value.size - offset_ptr; | ||
74 | + if (value.size > 0 && | ||
75 | + gvs_calculate_total_size (offset, offset_table_size / offset_size) != value.size) | ||
76 | + return FALSE; /* offset size not minimal */ | ||
77 | + | ||
78 | + return TRUE; | ||
79 | } | ||
80 | |||
81 | /* Variants {{{2 | ||
82 | diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c | ||
83 | index 44e4451..ad45043 100644 | ||
84 | --- a/glib/tests/gvariant.c | ||
85 | +++ b/glib/tests/gvariant.c | ||
86 | @@ -5076,6 +5076,86 @@ test_normal_checking_array_offsets2 (void) | ||
87 | g_variant_unref (variant); | ||
88 | } | ||
89 | |||
90 | +/* Test that an otherwise-valid serialised GVariant is considered non-normal if | ||
91 | + * its offset table entries are too wide. | ||
92 | + * | ||
93 | + * See §2.3.6 (Framing Offsets) of the GVariant specification. */ | ||
94 | +static void | ||
95 | +test_normal_checking_array_offsets_minimal_sized (void) | ||
96 | +{ | ||
97 | + GVariantBuilder builder; | ||
98 | + gsize i; | ||
99 | + GVariant *aay_constructed = NULL; | ||
100 | + const guint8 *data = NULL; | ||
101 | + guint8 *data_owned = NULL; | ||
102 | + GVariant *aay_deserialised = NULL; | ||
103 | + GVariant *aay_normalised = NULL; | ||
104 | + | ||
105 | + /* Construct an array of type aay, consisting of 128 elements which are each | ||
106 | + * an empty array, i.e. `[[] * 128]`. This is chosen because the inner | ||
107 | + * elements are variable sized (making the outer array variable sized, so it | ||
108 | + * must have an offset table), but they are also zero-sized when serialised. | ||
109 | + * So the serialised representation of @aay_constructed consists entirely of | ||
110 | + * its offset table, which is entirely zeroes. | ||
111 | + * | ||
112 | + * The array is chosen to be 128 elements long because that means offset | ||
113 | + * table entries which are 1 byte long. If the elements in the array were | ||
114 | + * non-zero-sized (to the extent that the overall array is ≥256 bytes long), | ||
115 | + * the offset table entries would end up being 2 bytes long. */ | ||
116 | + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay")); | ||
117 | + | ||
118 | + for (i = 0; i < 128; i++) | ||
119 | + g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0)); | ||
120 | + | ||
121 | + aay_constructed = g_variant_builder_end (&builder); | ||
122 | + | ||
123 | + /* Verify that the constructed array is in normal form, and its serialised | ||
124 | + * form is `b'\0' * 128`. */ | ||
125 | + g_assert_true (g_variant_is_normal_form (aay_constructed)); | ||
126 | + g_assert_cmpuint (g_variant_n_children (aay_constructed), ==, 128); | ||
127 | + g_assert_cmpuint (g_variant_get_size (aay_constructed), ==, 128); | ||
128 | + | ||
129 | + data = g_variant_get_data (aay_constructed); | ||
130 | + for (i = 0; i < g_variant_get_size (aay_constructed); i++) | ||
131 | + g_assert_cmpuint (data[i], ==, 0); | ||
132 | + | ||
133 | + /* Construct a serialised `aay` GVariant which is `b'\0' * 256`. This has to | ||
134 | + * be a non-normal form of `[[] * 128]`, with 2-byte-long offset table | ||
135 | + * entries, because each offset table entry has to be able to reference all of | ||
136 | + * the byte boundaries in the container. All the entries in the offset table | ||
137 | + * are zero, so all the elements of the array are zero-sized. */ | ||
138 | + data = data_owned = g_malloc0 (256); | ||
139 | + aay_deserialised = g_variant_new_from_data (G_VARIANT_TYPE ("aay"), | ||
140 | + data, | ||
141 | + 256, | ||
142 | + FALSE, | ||
143 | + g_free, | ||
144 | + g_steal_pointer (&data_owned)); | ||
145 | + | ||
146 | + g_assert_false (g_variant_is_normal_form (aay_deserialised)); | ||
147 | + g_assert_cmpuint (g_variant_n_children (aay_deserialised), ==, 128); | ||
148 | + g_assert_cmpuint (g_variant_get_size (aay_deserialised), ==, 256); | ||
149 | + | ||
150 | + data = g_variant_get_data (aay_deserialised); | ||
151 | + for (i = 0; i < g_variant_get_size (aay_deserialised); i++) | ||
152 | + g_assert_cmpuint (data[i], ==, 0); | ||
153 | + | ||
154 | + /* Get its normal form. That should change the serialised size. */ | ||
155 | + aay_normalised = g_variant_get_normal_form (aay_deserialised); | ||
156 | + | ||
157 | + g_assert_true (g_variant_is_normal_form (aay_normalised)); | ||
158 | + g_assert_cmpuint (g_variant_n_children (aay_normalised), ==, 128); | ||
159 | + g_assert_cmpuint (g_variant_get_size (aay_normalised), ==, 128); | ||
160 | + | ||
161 | + data = g_variant_get_data (aay_normalised); | ||
162 | + for (i = 0; i < g_variant_get_size (aay_normalised); i++) | ||
163 | + g_assert_cmpuint (data[i], ==, 0); | ||
164 | + | ||
165 | + g_variant_unref (aay_normalised); | ||
166 | + g_variant_unref (aay_deserialised); | ||
167 | + g_variant_unref (aay_constructed); | ||
168 | +} | ||
169 | + | ||
170 | /* Test that a tuple with invalidly large values in its offset table is | ||
171 | * normalised successfully without looping infinitely. */ | ||
172 | static void | ||
173 | @@ -5270,6 +5350,98 @@ test_normal_checking_tuple_offsets4 (void) | ||
174 | g_variant_unref (variant); | ||
175 | } | ||
176 | |||
177 | +/* Test that an otherwise-valid serialised GVariant is considered non-normal if | ||
178 | + * its offset table entries are too wide. | ||
179 | + * | ||
180 | + * See §2.3.6 (Framing Offsets) of the GVariant specification. */ | ||
181 | +static void | ||
182 | +test_normal_checking_tuple_offsets_minimal_sized (void) | ||
183 | +{ | ||
184 | + GString *type_string = NULL; | ||
185 | + GVariantBuilder builder; | ||
186 | + gsize i; | ||
187 | + GVariant *ray_constructed = NULL; | ||
188 | + const guint8 *data = NULL; | ||
189 | + guint8 *data_owned = NULL; | ||
190 | + GVariant *ray_deserialised = NULL; | ||
191 | + GVariant *ray_normalised = NULL; | ||
192 | + | ||
193 | + /* Construct a tuple of type (ay…ay), consisting of 129 members which are each | ||
194 | + * an empty array, i.e. `([] * 129)`. This is chosen because the inner | ||
195 | + * members are variable sized, so the outer tuple must have an offset table, | ||
196 | + * but they are also zero-sized when serialised. So the serialised | ||
197 | + * representation of @ray_constructed consists entirely of its offset table, | ||
198 | + * which is entirely zeroes. | ||
199 | + * | ||
200 | + * The tuple is chosen to be 129 members long because that means it has 128 | ||
201 | + * offset table entries which are 1 byte long each. If the members in the | ||
202 | + * tuple were non-zero-sized (to the extent that the overall tuple is ≥256 | ||
203 | + * bytes long), the offset table entries would end up being 2 bytes long. | ||
204 | + * | ||
205 | + * 129 members are used unlike 128 array elements in | ||
206 | + * test_normal_checking_array_offsets_minimal_sized(), because the last member | ||
207 | + * in a tuple never needs an offset table entry. */ | ||
208 | + type_string = g_string_new (""); | ||
209 | + g_string_append_c (type_string, '('); | ||
210 | + for (i = 0; i < 129; i++) | ||
211 | + g_string_append (type_string, "ay"); | ||
212 | + g_string_append_c (type_string, ')'); | ||
213 | + | ||
214 | + g_variant_builder_init (&builder, G_VARIANT_TYPE (type_string->str)); | ||
215 | + | ||
216 | + for (i = 0; i < 129; i++) | ||
217 | + g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0)); | ||
218 | + | ||
219 | + ray_constructed = g_variant_builder_end (&builder); | ||
220 | + | ||
221 | + /* Verify that the constructed tuple is in normal form, and its serialised | ||
222 | + * form is `b'\0' * 128`. */ | ||
223 | + g_assert_true (g_variant_is_normal_form (ray_constructed)); | ||
224 | + g_assert_cmpuint (g_variant_n_children (ray_constructed), ==, 129); | ||
225 | + g_assert_cmpuint (g_variant_get_size (ray_constructed), ==, 128); | ||
226 | + | ||
227 | + data = g_variant_get_data (ray_constructed); | ||
228 | + for (i = 0; i < g_variant_get_size (ray_constructed); i++) | ||
229 | + g_assert_cmpuint (data[i], ==, 0); | ||
230 | + | ||
231 | + /* Construct a serialised `(ay…ay)` GVariant which is `b'\0' * 256`. This has | ||
232 | + * to be a non-normal form of `([] * 129)`, with 2-byte-long offset table | ||
233 | + * entries, because each offset table entry has to be able to reference all of | ||
234 | + * the byte boundaries in the container. All the entries in the offset table | ||
235 | + * are zero, so all the members of the tuple are zero-sized. */ | ||
236 | + data = data_owned = g_malloc0 (256); | ||
237 | + ray_deserialised = g_variant_new_from_data (G_VARIANT_TYPE (type_string->str), | ||
238 | + data, | ||
239 | + 256, | ||
240 | + FALSE, | ||
241 | + g_free, | ||
242 | + g_steal_pointer (&data_owned)); | ||
243 | + | ||
244 | + g_assert_false (g_variant_is_normal_form (ray_deserialised)); | ||
245 | + g_assert_cmpuint (g_variant_n_children (ray_deserialised), ==, 129); | ||
246 | + g_assert_cmpuint (g_variant_get_size (ray_deserialised), ==, 256); | ||
247 | + | ||
248 | + data = g_variant_get_data (ray_deserialised); | ||
249 | + for (i = 0; i < g_variant_get_size (ray_deserialised); i++) | ||
250 | + g_assert_cmpuint (data[i], ==, 0); | ||
251 | + | ||
252 | + /* Get its normal form. That should change the serialised size. */ | ||
253 | + ray_normalised = g_variant_get_normal_form (ray_deserialised); | ||
254 | + | ||
255 | + g_assert_true (g_variant_is_normal_form (ray_normalised)); | ||
256 | + g_assert_cmpuint (g_variant_n_children (ray_normalised), ==, 129); | ||
257 | + g_assert_cmpuint (g_variant_get_size (ray_normalised), ==, 128); | ||
258 | + | ||
259 | + data = g_variant_get_data (ray_normalised); | ||
260 | + for (i = 0; i < g_variant_get_size (ray_normalised); i++) | ||
261 | + g_assert_cmpuint (data[i], ==, 0); | ||
262 | + | ||
263 | + g_variant_unref (ray_normalised); | ||
264 | + g_variant_unref (ray_deserialised); | ||
265 | + g_variant_unref (ray_constructed); | ||
266 | + g_string_free (type_string, TRUE); | ||
267 | +} | ||
268 | + | ||
269 | /* Test that an empty object path is normalised successfully to the base object | ||
270 | * path, ‘/’. */ | ||
271 | static void | ||
272 | @@ -5414,6 +5586,8 @@ main (int argc, char **argv) | ||
273 | test_normal_checking_array_offsets); | ||
274 | g_test_add_func ("/gvariant/normal-checking/array-offsets2", | ||
275 | test_normal_checking_array_offsets2); | ||
276 | + g_test_add_func ("/gvariant/normal-checking/array-offsets/minimal-sized", | ||
277 | + test_normal_checking_array_offsets_minimal_sized); | ||
278 | g_test_add_func ("/gvariant/normal-checking/tuple-offsets", | ||
279 | test_normal_checking_tuple_offsets); | ||
280 | g_test_add_func ("/gvariant/normal-checking/tuple-offsets2", | ||
281 | @@ -5422,6 +5596,8 @@ main (int argc, char **argv) | ||
282 | test_normal_checking_tuple_offsets3); | ||
283 | g_test_add_func ("/gvariant/normal-checking/tuple-offsets4", | ||
284 | test_normal_checking_tuple_offsets4); | ||
285 | + g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized", | ||
286 | + test_normal_checking_tuple_offsets_minimal_sized); | ||
287 | g_test_add_func ("/gvariant/normal-checking/empty-object-path", | ||
288 | test_normal_checking_empty_object_path); | ||
289 | |||
290 | -- | ||
291 | 2.40.0 | ||
diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0001.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0001.patch new file mode 100644 index 0000000000..cc4b4055b2 --- /dev/null +++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0001.patch | |||
@@ -0,0 +1,97 @@ | |||
1 | From 4c4cf568f0f710baf0bd04d52df715636bc6b971 Mon Sep 17 00:00:00 2001 | ||
2 | From: Philip Withnall <pwithnall@endlessos.org> | ||
3 | Date: Thu, 17 Aug 2023 04:23:41 +0000 | ||
4 | Subject: [PATCH] gvariant: Fix g_variant_byteswap() returning non-normal data | ||
5 | |||
6 | If `g_variant_byteswap()` was called on a non-normal variant of a type | ||
7 | which doesn’t need byteswapping, it would return a non-normal output. | ||
8 | |||
9 | That contradicts the documentation, which says that the return value is | ||
10 | always in normal form. | ||
11 | |||
12 | Fix the code so it matches the documentation. | ||
13 | |||
14 | Includes a unit test. | ||
15 | |||
16 | Signed-off-by: Philip Withnall <pwithnall@endlessos.org> | ||
17 | |||
18 | Helps: #2797 | ||
19 | |||
20 | CVE: CVE-2023-32611 | ||
21 | |||
22 | Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/glib/-/commit/4c4cf568f0f710baf0bd04d52df715636bc6b971] | ||
23 | |||
24 | Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> | ||
25 | --- | ||
26 | glib/gvariant.c | 8 +++++--- | ||
27 | glib/tests/gvariant.c | 24 ++++++++++++++++++++++++ | ||
28 | 2 files changed, 29 insertions(+), 3 deletions(-) | ||
29 | |||
30 | diff --git a/glib/gvariant.c b/glib/gvariant.c | ||
31 | index 30a3280..7e568d1 100644 | ||
32 | --- a/glib/gvariant.c | ||
33 | +++ b/glib/gvariant.c | ||
34 | @@ -6004,14 +6004,16 @@ g_variant_byteswap (GVariant *value) | ||
35 | g_variant_serialised_byteswap (serialised); | ||
36 | |||
37 | bytes = g_bytes_new_take (serialised.data, serialised.size); | ||
38 | - new = g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE); | ||
39 | + new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE)); | ||
40 | g_bytes_unref (bytes); | ||
41 | } | ||
42 | else | ||
43 | /* contains no multi-byte data */ | ||
44 | - new = value; | ||
45 | + new = g_variant_get_normal_form (value); | ||
46 | |||
47 | - return g_variant_ref_sink (new); | ||
48 | + g_assert (g_variant_is_trusted (new)); | ||
49 | + | ||
50 | + return g_steal_pointer (&new); | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c | ||
55 | index ad45043..36c86c2 100644 | ||
56 | --- a/glib/tests/gvariant.c | ||
57 | +++ b/glib/tests/gvariant.c | ||
58 | @@ -3818,6 +3818,29 @@ test_gv_byteswap (void) | ||
59 | g_free (string); | ||
60 | } | ||
61 | |||
62 | +static void | ||
63 | +test_gv_byteswap_non_normal_non_aligned (void) | ||
64 | +{ | ||
65 | + const guint8 data[] = { 0x02 }; | ||
66 | + GVariant *v = NULL; | ||
67 | + GVariant *v_byteswapped = NULL; | ||
68 | + | ||
69 | + g_test_summary ("Test that calling g_variant_byteswap() on a variant which " | ||
70 | + "is in non-normal form and doesn’t need byteswapping returns " | ||
71 | + "the same variant in normal form."); | ||
72 | + | ||
73 | + v = g_variant_new_from_data (G_VARIANT_TYPE_BOOLEAN, data, sizeof (data), FALSE, NULL, NULL); | ||
74 | + g_assert_false (g_variant_is_normal_form (v)); | ||
75 | + | ||
76 | + v_byteswapped = g_variant_byteswap (v); | ||
77 | + g_assert_true (g_variant_is_normal_form (v_byteswapped)); | ||
78 | + | ||
79 | + g_assert_cmpvariant (v, v_byteswapped); | ||
80 | + | ||
81 | + g_variant_unref (v); | ||
82 | + g_variant_unref (v_byteswapped); | ||
83 | +} | ||
84 | + | ||
85 | static void | ||
86 | test_parser (void) | ||
87 | { | ||
88 | @@ -5553,6 +5576,7 @@ main (int argc, char **argv) | ||
89 | g_test_add_func ("/gvariant/builder-memory", test_builder_memory); | ||
90 | g_test_add_func ("/gvariant/hashing", test_hashing); | ||
91 | g_test_add_func ("/gvariant/byteswap", test_gv_byteswap); | ||
92 | + g_test_add_func ("/gvariant/byteswap/non-normal-non-aligned", test_gv_byteswap_non_normal_non_aligned); | ||
93 | g_test_add_func ("/gvariant/parser", test_parses); | ||
94 | g_test_add_func ("/gvariant/parser/integer-bounds", test_parser_integer_bounds); | ||
95 | g_test_add_func ("/gvariant/parser/recursion", test_parser_recursion); | ||
96 | -- | ||
97 | 2.40.0 | ||
diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0002.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0002.patch new file mode 100644 index 0000000000..304c15bceb --- /dev/null +++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0002.patch | |||
@@ -0,0 +1,282 @@ | |||
1 | From 7d7efce1d9c379fdd7d2ff58caea88f8806fdd2e Mon Sep 17 00:00:00 2001 | ||
2 | From: Philip Withnall <pwithnall@endlessos.org> | ||
3 | Date: Thu, 17 Aug 2023 05:05:39 +0000 | ||
4 | Subject: [PATCH] gvariant: Allow g_variant_byteswap() to operate on tree-form | ||
5 | variants | ||
6 | |||
7 | This avoids needing to always serialise a variant before byteswapping it. | ||
8 | With variants in non-normal forms, serialisation can result in a large | ||
9 | increase in size of the variant, and a lot of allocations for leaf | ||
10 | `GVariant`s. This can lead to a denial of service attack. | ||
11 | |||
12 | Avoid that by changing byteswapping so that it happens on the tree form | ||
13 | of the variant if the input is in non-normal form. If the input is in | ||
14 | normal form (either serialised or in tree form), continue using the | ||
15 | existing code as byteswapping an already-serialised normal variant is | ||
16 | about 3× faster than byteswapping on the equivalent tree form. | ||
17 | |||
18 | The existing unit tests cover byteswapping well, but need some | ||
19 | adaptation so that they operate on tree form variants too. | ||
20 | |||
21 | I considered dropping the serialised byteswapping code and doing all | ||
22 | byteswapping on tree-form variants, as that would make maintenance | ||
23 | simpler (avoiding having two parallel implementations of byteswapping). | ||
24 | However, most inputs to `g_variant_byteswap()` are likely to be | ||
25 | serialised variants (coming from a byte array of input from some foreign | ||
26 | source) and most of them are going to be in normal form (as corruption | ||
27 | and malicious action are rare). So getting rid of the serialised | ||
28 | byteswapping code would impose quite a performance penalty on the common | ||
29 | case. | ||
30 | |||
31 | Signed-off-by: Philip Withnall <pwithnall@endlessos.org> | ||
32 | |||
33 | Fixes: #2797 | ||
34 | |||
35 | CVE: CVE-2023-32611 | ||
36 | |||
37 | Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/glib/-/commit/7d7efce1d9c379fdd7d2ff58caea88f8806fdd2e] | ||
38 | |||
39 | Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> | ||
40 | --- | ||
41 | glib/gvariant.c | 83 ++++++++++++++++++++++++++++++++----------- | ||
42 | glib/tests/gvariant.c | 57 +++++++++++++++++++++++++---- | ||
43 | 2 files changed, 113 insertions(+), 27 deletions(-) | ||
44 | |||
45 | diff --git a/glib/gvariant.c b/glib/gvariant.c | ||
46 | index 7e568d1..65b8443 100644 | ||
47 | --- a/glib/gvariant.c | ||
48 | +++ b/glib/gvariant.c | ||
49 | @@ -5839,7 +5839,8 @@ g_variant_iter_loop (GVariantIter *iter, | ||
50 | |||
51 | /* Serialized data {{{1 */ | ||
52 | static GVariant * | ||
53 | -g_variant_deep_copy (GVariant *value) | ||
54 | +g_variant_deep_copy (GVariant *value, | ||
55 | + gboolean byteswap) | ||
56 | { | ||
57 | switch (g_variant_classify (value)) | ||
58 | { | ||
59 | @@ -5857,7 +5858,7 @@ g_variant_deep_copy (GVariant *value) | ||
60 | for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++) | ||
61 | { | ||
62 | GVariant *child = g_variant_get_child_value (value, i); | ||
63 | - g_variant_builder_add_value (&builder, g_variant_deep_copy (child)); | ||
64 | + g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap)); | ||
65 | g_variant_unref (child); | ||
66 | } | ||
67 | |||
68 | @@ -5871,28 +5872,63 @@ g_variant_deep_copy (GVariant *value) | ||
69 | return g_variant_new_byte (g_variant_get_byte (value)); | ||
70 | |||
71 | case G_VARIANT_CLASS_INT16: | ||
72 | - return g_variant_new_int16 (g_variant_get_int16 (value)); | ||
73 | + if (byteswap) | ||
74 | + return g_variant_new_int16 (GUINT16_SWAP_LE_BE (g_variant_get_int16 (value))); | ||
75 | + else | ||
76 | + return g_variant_new_int16 (g_variant_get_int16 (value)); | ||
77 | |||
78 | case G_VARIANT_CLASS_UINT16: | ||
79 | - return g_variant_new_uint16 (g_variant_get_uint16 (value)); | ||
80 | + if (byteswap) | ||
81 | + return g_variant_new_uint16 (GUINT16_SWAP_LE_BE (g_variant_get_uint16 (value))); | ||
82 | + else | ||
83 | + return g_variant_new_uint16 (g_variant_get_uint16 (value)); | ||
84 | |||
85 | case G_VARIANT_CLASS_INT32: | ||
86 | - return g_variant_new_int32 (g_variant_get_int32 (value)); | ||
87 | + if (byteswap) | ||
88 | + return g_variant_new_int32 (GUINT32_SWAP_LE_BE (g_variant_get_int32 (value))); | ||
89 | + else | ||
90 | + return g_variant_new_int32 (g_variant_get_int32 (value)); | ||
91 | |||
92 | case G_VARIANT_CLASS_UINT32: | ||
93 | - return g_variant_new_uint32 (g_variant_get_uint32 (value)); | ||
94 | + if (byteswap) | ||
95 | + return g_variant_new_uint32 (GUINT32_SWAP_LE_BE (g_variant_get_uint32 (value))); | ||
96 | + else | ||
97 | + return g_variant_new_uint32 (g_variant_get_uint32 (value)); | ||
98 | |||
99 | case G_VARIANT_CLASS_INT64: | ||
100 | - return g_variant_new_int64 (g_variant_get_int64 (value)); | ||
101 | + if (byteswap) | ||
102 | + return g_variant_new_int64 (GUINT64_SWAP_LE_BE (g_variant_get_int64 (value))); | ||
103 | + else | ||
104 | + return g_variant_new_int64 (g_variant_get_int64 (value)); | ||
105 | |||
106 | case G_VARIANT_CLASS_UINT64: | ||
107 | - return g_variant_new_uint64 (g_variant_get_uint64 (value)); | ||
108 | + if (byteswap) | ||
109 | + return g_variant_new_uint64 (GUINT64_SWAP_LE_BE (g_variant_get_uint64 (value))); | ||
110 | + else | ||
111 | + return g_variant_new_uint64 (g_variant_get_uint64 (value)); | ||
112 | |||
113 | case G_VARIANT_CLASS_HANDLE: | ||
114 | - return g_variant_new_handle (g_variant_get_handle (value)); | ||
115 | + if (byteswap) | ||
116 | + return g_variant_new_handle (GUINT32_SWAP_LE_BE (g_variant_get_handle (value))); | ||
117 | + else | ||
118 | + return g_variant_new_handle (g_variant_get_handle (value)); | ||
119 | |||
120 | case G_VARIANT_CLASS_DOUBLE: | ||
121 | - return g_variant_new_double (g_variant_get_double (value)); | ||
122 | + if (byteswap) | ||
123 | + { | ||
124 | + /* We have to convert the double to a uint64 here using a union, | ||
125 | + * because a cast will round it numerically. */ | ||
126 | + union | ||
127 | + { | ||
128 | + guint64 u64; | ||
129 | + gdouble dbl; | ||
130 | + } u1, u2; | ||
131 | + u1.dbl = g_variant_get_double (value); | ||
132 | + u2.u64 = GUINT64_SWAP_LE_BE (u1.u64); | ||
133 | + return g_variant_new_double (u2.dbl); | ||
134 | + } | ||
135 | + else | ||
136 | + return g_variant_new_double (g_variant_get_double (value)); | ||
137 | |||
138 | case G_VARIANT_CLASS_STRING: | ||
139 | return g_variant_new_string (g_variant_get_string (value, NULL)); | ||
140 | @@ -5947,7 +5983,7 @@ g_variant_get_normal_form (GVariant *value) | ||
141 | if (g_variant_is_normal_form (value)) | ||
142 | return g_variant_ref (value); | ||
143 | |||
144 | - trusted = g_variant_deep_copy (value); | ||
145 | + trusted = g_variant_deep_copy (value, FALSE); | ||
146 | g_assert (g_variant_is_trusted (trusted)); | ||
147 | |||
148 | return g_variant_ref_sink (trusted); | ||
149 | @@ -5967,6 +6003,11 @@ g_variant_get_normal_form (GVariant *value) | ||
150 | * contain multi-byte numeric data. That include strings, booleans, | ||
151 | * bytes and containers containing only these things (recursively). | ||
152 | * | ||
153 | + * While this function can safely handle untrusted, non-normal data, it is | ||
154 | + * recommended to check whether the input is in normal form beforehand, using | ||
155 | + * g_variant_is_normal_form(), and to reject non-normal inputs if your | ||
156 | + * application can be strict about what inputs it rejects. | ||
157 | + * | ||
158 | * The returned value is always in normal form and is marked as trusted. | ||
159 | * | ||
160 | * Returns: (transfer full): the byteswapped form of @value | ||
161 | @@ -5984,22 +6025,21 @@ g_variant_byteswap (GVariant *value) | ||
162 | |||
163 | g_variant_type_info_query (type_info, &alignment, NULL); | ||
164 | |||
165 | - if (alignment) | ||
166 | - /* (potentially) contains multi-byte numeric data */ | ||
167 | + if (alignment && g_variant_is_normal_form (value)) | ||
168 | { | ||
169 | + /* (potentially) contains multi-byte numeric data, but is also already in | ||
170 | + * normal form so we can use a faster byteswapping codepath on the | ||
171 | + * serialised data */ | ||
172 | GVariantSerialised serialised = { 0, }; | ||
173 | - GVariant *trusted; | ||
174 | GBytes *bytes; | ||
175 | |||
176 | - trusted = g_variant_get_normal_form (value); | ||
177 | - serialised.type_info = g_variant_get_type_info (trusted); | ||
178 | - serialised.size = g_variant_get_size (trusted); | ||
179 | + serialised.type_info = g_variant_get_type_info (value); | ||
180 | + serialised.size = g_variant_get_size (value); | ||
181 | serialised.data = g_malloc (serialised.size); | ||
182 | - serialised.depth = g_variant_get_depth (trusted); | ||
183 | + serialised.depth = g_variant_get_depth (value); | ||
184 | serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */ | ||
185 | serialised.checked_offsets_up_to = G_MAXSIZE; | ||
186 | - g_variant_store (trusted, serialised.data); | ||
187 | - g_variant_unref (trusted); | ||
188 | + g_variant_store (value, serialised.data); | ||
189 | |||
190 | g_variant_serialised_byteswap (serialised); | ||
191 | |||
192 | @@ -6007,6 +6047,9 @@ g_variant_byteswap (GVariant *value) | ||
193 | new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE)); | ||
194 | g_bytes_unref (bytes); | ||
195 | } | ||
196 | + else if (alignment) | ||
197 | + /* (potentially) contains multi-byte numeric data */ | ||
198 | + new = g_variant_ref_sink (g_variant_deep_copy (value, TRUE)); | ||
199 | else | ||
200 | /* contains no multi-byte data */ | ||
201 | new = g_variant_get_normal_form (value); | ||
202 | diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c | ||
203 | index 36c86c2..43091f2 100644 | ||
204 | --- a/glib/tests/gvariant.c | ||
205 | +++ b/glib/tests/gvariant.c | ||
206 | @@ -2280,24 +2280,67 @@ serialise_tree (TreeInstance *tree, | ||
207 | static void | ||
208 | test_byteswap (void) | ||
209 | { | ||
210 | - GVariantSerialised one = { 0, }, two = { 0, }; | ||
211 | + GVariantSerialised one = { 0, }, two = { 0, }, three = { 0, }; | ||
212 | TreeInstance *tree; | ||
213 | - | ||
214 | + GVariant *one_variant = NULL; | ||
215 | + GVariant *two_variant = NULL; | ||
216 | + GVariant *two_byteswapped = NULL; | ||
217 | + GVariant *three_variant = NULL; | ||
218 | + GVariant *three_byteswapped = NULL; | ||
219 | + guint8 *three_data_copy = NULL; | ||
220 | + gsize three_size_copy = 0; | ||
221 | + | ||
222 | + /* Write a tree out twice, once normally and once byteswapped. */ | ||
223 | tree = tree_instance_new (NULL, 3); | ||
224 | serialise_tree (tree, &one); | ||
225 | |||
226 | + one_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (one.type_info)), | ||
227 | + one.data, one.size, FALSE, NULL, NULL); | ||
228 | + | ||
229 | i_am_writing_byteswapped = TRUE; | ||
230 | serialise_tree (tree, &two); | ||
231 | + serialise_tree (tree, &three); | ||
232 | i_am_writing_byteswapped = FALSE; | ||
233 | |||
234 | - g_variant_serialised_byteswap (two); | ||
235 | - | ||
236 | - g_assert_cmpmem (one.data, one.size, two.data, two.size); | ||
237 | - g_assert_cmpuint (one.depth, ==, two.depth); | ||
238 | - | ||
239 | + /* Swap the first byteswapped one back using the function we want to test. */ | ||
240 | + two_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (two.type_info)), | ||
241 | + two.data, two.size, FALSE, NULL, NULL); | ||
242 | + two_byteswapped = g_variant_byteswap (two_variant); | ||
243 | + | ||
244 | + /* Make the second byteswapped one non-normal (hopefully), and then byteswap | ||
245 | + * it back using the function we want to test in its non-normal mode. | ||
246 | + * This might not work because it’s not necessarily possible to make an | ||
247 | + * arbitrary random variant non-normal. Adding a single zero byte to the end | ||
248 | + * often makes something non-normal but still readable. */ | ||
249 | + three_size_copy = three.size + 1; | ||
250 | + three_data_copy = g_malloc (three_size_copy); | ||
251 | + memcpy (three_data_copy, three.data, three.size); | ||
252 | + three_data_copy[three.size] = '\0'; | ||
253 | + | ||
254 | + three_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (three.type_info)), | ||
255 | + three_data_copy, three_size_copy, FALSE, NULL, NULL); | ||
256 | + three_byteswapped = g_variant_byteswap (three_variant); | ||
257 | + | ||
258 | + /* Check they’re the same. We can always compare @one_variant and | ||
259 | + * @two_byteswapped. We can only compare @two_byteswapped and | ||
260 | + * @three_byteswapped if @two_variant and @three_variant are equal: in that | ||
261 | + * case, the corruption to @three_variant was enough to make it non-normal but | ||
262 | + * not enough to change its value. */ | ||
263 | + g_assert_cmpvariant (one_variant, two_byteswapped); | ||
264 | + | ||
265 | + if (g_variant_equal (two_variant, three_variant)) | ||
266 | + g_assert_cmpvariant (two_byteswapped, three_byteswapped); | ||
267 | + | ||
268 | + g_variant_unref (three_byteswapped); | ||
269 | + g_variant_unref (three_variant); | ||
270 | + g_variant_unref (two_byteswapped); | ||
271 | + g_variant_unref (two_variant); | ||
272 | + g_variant_unref (one_variant); | ||
273 | tree_instance_free (tree); | ||
274 | g_free (one.data); | ||
275 | g_free (two.data); | ||
276 | + g_free (three.data); | ||
277 | + g_free (three_data_copy); | ||
278 | } | ||
279 | |||
280 | static void | ||
281 | -- | ||
282 | 2.40.0 | ||
diff --git a/meta/recipes-core/glib-2.0/glib-2.0_2.72.3.bb b/meta/recipes-core/glib-2.0/glib-2.0_2.72.3.bb index b04b5f0a44..3545e6675a 100644 --- a/meta/recipes-core/glib-2.0/glib-2.0_2.72.3.bb +++ b/meta/recipes-core/glib-2.0/glib-2.0_2.72.3.bb | |||
@@ -26,6 +26,9 @@ SRC_URI = "${GNOME_MIRROR}/glib/${SHRT_VER}/glib-${PV}.tar.xz \ | |||
26 | file://CVE-2023-32665-0007.patch \ | 26 | file://CVE-2023-32665-0007.patch \ |
27 | file://CVE-2023-32665-0008.patch \ | 27 | file://CVE-2023-32665-0008.patch \ |
28 | file://CVE-2023-32665-0009.patch \ | 28 | file://CVE-2023-32665-0009.patch \ |
29 | file://CVE-2023-29499.patch \ | ||
30 | file://CVE-2023-32611-0001.patch \ | ||
31 | file://CVE-2023-32611-0002.patch \ | ||
29 | " | 32 | " |
30 | SRC_URI:append:class-native = " file://relocate-modules.patch" | 33 | SRC_URI:append:class-native = " file://relocate-modules.patch" |
31 | 34 | ||