diff options
| author | Soumya Sambu <soumya.sambu@windriver.com> | 2023-08-22 10:02:29 +0000 |
|---|---|---|
| committer | Steve Sakoman <steve@sakoman.com> | 2023-08-26 04:24:02 -1000 |
| commit | aae7997aeaaaee02d05600d68db45d3b80b477a3 (patch) | |
| tree | 4941592c323ba8138b48aba0dbf0ce09eaa6b8e3 | |
| parent | f51146f6eeb6089832ff992b8c1216de71250cbb (diff) | |
| download | poky-aae7997aeaaaee02d05600d68db45d3b80b477a3.tar.gz | |
glib-2.0: Fix CVE-2023-29499 and CVE-2023-32611
GVariant offset table entry size is not checked in is_normal()
g_variant_byteswap() can take a long time with some non-normal inputs
(From OE-Core rev: 5ed552ce97e22b449c1036f6c58944ab26db2f0d)
Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
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 | ||
