1b5975d6bSopenharmony_ciFrom 78da5faccb3e065116b75b3ff87ff55381da6c76 Mon Sep 17 00:00:00 2001 2b5975d6bSopenharmony_ciFrom: Philip Withnall <pwithnall@endlessos.org> 3b5975d6bSopenharmony_ciDate: Thu, 15 Dec 2022 13:00:39 +0000 4b5975d6bSopenharmony_ciSubject: [PATCH 1/2] =?UTF-8?q?gvariant:=20Check=20offset=20table=20doesn?= 5b5975d6bSopenharmony_ci =?UTF-8?q?=E2=80=99t=20fall=20outside=20variant=20bounds?= 6b5975d6bSopenharmony_ciMIME-Version: 1.0 7b5975d6bSopenharmony_ciContent-Type: text/plain; charset=UTF-8 8b5975d6bSopenharmony_ciContent-Transfer-Encoding: 8bit 9b5975d6bSopenharmony_ci 10b5975d6bSopenharmony_ciWhen dereferencing the first entry in the offset table for a tuple, 11b5975d6bSopenharmony_cicheck that it doesn’t fall outside the bounds of the variant first. 12b5975d6bSopenharmony_ci 13b5975d6bSopenharmony_ciThis prevents an out-of-bounds read from some non-normal tuples. 14b5975d6bSopenharmony_ci 15b5975d6bSopenharmony_ciThis bug was introduced in commit 73d0aa81c2575a5c9ae77d. 16b5975d6bSopenharmony_ci 17b5975d6bSopenharmony_ciIncludes a unit test, although the test will likely only catch the 18b5975d6bSopenharmony_cioriginal bug if run with asan enabled. 19b5975d6bSopenharmony_ci 20b5975d6bSopenharmony_ciSigned-off-by: Philip Withnall <pwithnall@endlessos.org> 21b5975d6bSopenharmony_ci 22b5975d6bSopenharmony_ciFixes: #2840 23b5975d6bSopenharmony_cioss-fuzz#54302 24b5975d6bSopenharmony_ci--- 25b5975d6bSopenharmony_ci glib/gvariant-serialiser.c | 12 ++++++-- 26b5975d6bSopenharmony_ci glib/tests/gvariant.c | 63 ++++++++++++++++++++++++++++++++++++++ 27b5975d6bSopenharmony_ci 2 files changed, 72 insertions(+), 3 deletions(-) 28b5975d6bSopenharmony_ci 29b5975d6bSopenharmony_cidiff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c 30b5975d6bSopenharmony_ciindex f443c2eb85..4e4a73ad17 100644 31b5975d6bSopenharmony_ci--- a/glib/gvariant-serialiser.c 32b5975d6bSopenharmony_ci+++ b/glib/gvariant-serialiser.c 33b5975d6bSopenharmony_ci@@ -984,7 +984,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised value, 34b5975d6bSopenharmony_ci 35b5975d6bSopenharmony_ci member_info = g_variant_type_info_member_info (value.type_info, index_); 36b5975d6bSopenharmony_ci 37b5975d6bSopenharmony_ci- if (member_info->i + 1) 38b5975d6bSopenharmony_ci+ if (member_info->i + 1 && 39b5975d6bSopenharmony_ci+ offset_size * (member_info->i + 1) <= value.size) 40b5975d6bSopenharmony_ci member_start = gvs_read_unaligned_le (value.data + value.size - 41b5975d6bSopenharmony_ci offset_size * (member_info->i + 1), 42b5975d6bSopenharmony_ci offset_size); 43b5975d6bSopenharmony_ci@@ -995,7 +996,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised value, 44b5975d6bSopenharmony_ci member_start &= member_info->b; 45b5975d6bSopenharmony_ci member_start |= member_info->c; 46b5975d6bSopenharmony_ci 47b5975d6bSopenharmony_ci- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST) 48b5975d6bSopenharmony_ci+ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST && 49b5975d6bSopenharmony_ci+ offset_size * (member_info->i + 1) <= value.size) 50b5975d6bSopenharmony_ci member_end = value.size - offset_size * (member_info->i + 1); 51b5975d6bSopenharmony_ci 52b5975d6bSopenharmony_ci else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED) 53b5975d6bSopenharmony_ci@@ -1006,11 +1008,15 @@ gvs_tuple_get_member_bounds (GVariantSerialised value, 54b5975d6bSopenharmony_ci member_end = member_start + fixed_size; 55b5975d6bSopenharmony_ci } 56b5975d6bSopenharmony_ci 57b5975d6bSopenharmony_ci- else /* G_VARIANT_MEMBER_ENDING_OFFSET */ 58b5975d6bSopenharmony_ci+ else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET && 59b5975d6bSopenharmony_ci+ offset_size * (member_info->i + 2) <= value.size) 60b5975d6bSopenharmony_ci member_end = gvs_read_unaligned_le (value.data + value.size - 61b5975d6bSopenharmony_ci offset_size * (member_info->i + 2), 62b5975d6bSopenharmony_ci offset_size); 63b5975d6bSopenharmony_ci 64b5975d6bSopenharmony_ci+ else /* invalid */ 65b5975d6bSopenharmony_ci+ member_end = G_MAXSIZE; 66b5975d6bSopenharmony_ci+ 67b5975d6bSopenharmony_ci if (out_member_start != NULL) 68b5975d6bSopenharmony_ci *out_member_start = member_start; 69b5975d6bSopenharmony_ci if (out_member_end != NULL) 70b5975d6bSopenharmony_cidiff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c 71b5975d6bSopenharmony_ciindex b360888e4d..98c51a1d75 100644 72b5975d6bSopenharmony_ci--- a/glib/tests/gvariant.c 73b5975d6bSopenharmony_ci+++ b/glib/tests/gvariant.c 74b5975d6bSopenharmony_ci@@ -5576,6 +5576,67 @@ test_normal_checking_tuple_offsets4 (void) 75b5975d6bSopenharmony_ci g_variant_unref (variant); 76b5975d6bSopenharmony_ci } 77b5975d6bSopenharmony_ci 78b5975d6bSopenharmony_ci+/* This is a regression test that dereferencing the first element in the offset 79b5975d6bSopenharmony_ci+ * table doesn’t dereference memory before the start of the GVariant. The first 80b5975d6bSopenharmony_ci+ * element in the offset table gives the offset of the final member in the 81b5975d6bSopenharmony_ci+ * tuple (the offset table is stored in reverse), and the position of this final 82b5975d6bSopenharmony_ci+ * member is needed to check that none of the tuple members overlap with the 83b5975d6bSopenharmony_ci+ * offset table 84b5975d6bSopenharmony_ci+ * 85b5975d6bSopenharmony_ci+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2840 */ 86b5975d6bSopenharmony_ci+static void 87b5975d6bSopenharmony_ci+test_normal_checking_tuple_offsets5 (void) 88b5975d6bSopenharmony_ci+{ 89b5975d6bSopenharmony_ci+ /* A tuple of type (sss) in normal form would have an offset table with two 90b5975d6bSopenharmony_ci+ * entries: 91b5975d6bSopenharmony_ci+ * - The first entry (lowest index in the table) gives the offset of the 92b5975d6bSopenharmony_ci+ * third `s` in the tuple, as the offset table is reversed compared to the 93b5975d6bSopenharmony_ci+ * tuple members. 94b5975d6bSopenharmony_ci+ * - The second entry (highest index in the table) gives the offset of the 95b5975d6bSopenharmony_ci+ * second `s` in the tuple. 96b5975d6bSopenharmony_ci+ * - The offset of the first `s` in the tuple is always 0. 97b5975d6bSopenharmony_ci+ * 98b5975d6bSopenharmony_ci+ * See §2.5.4 (Structures) of the GVariant specification for details, noting 99b5975d6bSopenharmony_ci+ * that the table is only layed out this way because all three members of the 100b5975d6bSopenharmony_ci+ * tuple have non-fixed sizes. 101b5975d6bSopenharmony_ci+ * 102b5975d6bSopenharmony_ci+ * It’s not clear whether the 0xaa data of this variant is part of the strings 103b5975d6bSopenharmony_ci+ * in the tuple, or part of the offset table. It doesn’t really matter. This 104b5975d6bSopenharmony_ci+ * is a regression test to check that the code to validate the offset table 105b5975d6bSopenharmony_ci+ * doesn’t unconditionally try to access the first entry in the offset table 106b5975d6bSopenharmony_ci+ * by subtracting the table size from the end of the GVariant data. 107b5975d6bSopenharmony_ci+ * 108b5975d6bSopenharmony_ci+ * In this non-normal case, that would result in an address off the start of 109b5975d6bSopenharmony_ci+ * the GVariant data, and an out-of-bounds read, because the GVariant is one 110b5975d6bSopenharmony_ci+ * byte long, but the offset table is calculated as two bytes long (with 1B 111b5975d6bSopenharmony_ci+ * sized entries) from the tuple’s type. 112b5975d6bSopenharmony_ci+ */ 113b5975d6bSopenharmony_ci+ const GVariantType *data_type = G_VARIANT_TYPE ("(sss)"); 114b5975d6bSopenharmony_ci+ const guint8 data[] = { 0xaa }; 115b5975d6bSopenharmony_ci+ gsize size = sizeof (data); 116b5975d6bSopenharmony_ci+ GVariant *variant = NULL; 117b5975d6bSopenharmony_ci+ GVariant *normal_variant = NULL; 118b5975d6bSopenharmony_ci+ GVariant *expected = NULL; 119b5975d6bSopenharmony_ci+ 120b5975d6bSopenharmony_ci+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2840"); 121b5975d6bSopenharmony_ci+ 122b5975d6bSopenharmony_ci+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL); 123b5975d6bSopenharmony_ci+ g_assert_nonnull (variant); 124b5975d6bSopenharmony_ci+ 125b5975d6bSopenharmony_ci+ g_assert_false (g_variant_is_normal_form (variant)); 126b5975d6bSopenharmony_ci+ 127b5975d6bSopenharmony_ci+ normal_variant = g_variant_get_normal_form (variant); 128b5975d6bSopenharmony_ci+ g_assert_nonnull (normal_variant); 129b5975d6bSopenharmony_ci+ 130b5975d6bSopenharmony_ci+ expected = g_variant_new_parsed ("('', '', '')"); 131b5975d6bSopenharmony_ci+ g_assert_cmpvariant (expected, variant); 132b5975d6bSopenharmony_ci+ g_assert_cmpvariant (expected, normal_variant); 133b5975d6bSopenharmony_ci+ 134b5975d6bSopenharmony_ci+ g_variant_unref (expected); 135b5975d6bSopenharmony_ci+ g_variant_unref (normal_variant); 136b5975d6bSopenharmony_ci+ g_variant_unref (variant); 137b5975d6bSopenharmony_ci+} 138b5975d6bSopenharmony_ci+ 139b5975d6bSopenharmony_ci /* Test that an otherwise-valid serialised GVariant is considered non-normal if 140b5975d6bSopenharmony_ci * its offset table entries are too wide. 141b5975d6bSopenharmony_ci * 142b5975d6bSopenharmony_ci@@ -5827,6 +5888,8 @@ main (int argc, char **argv) 143b5975d6bSopenharmony_ci test_normal_checking_tuple_offsets3); 144b5975d6bSopenharmony_ci g_test_add_func ("/gvariant/normal-checking/tuple-offsets4", 145b5975d6bSopenharmony_ci test_normal_checking_tuple_offsets4); 146b5975d6bSopenharmony_ci+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets5", 147b5975d6bSopenharmony_ci+ test_normal_checking_tuple_offsets5); 148b5975d6bSopenharmony_ci g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized", 149b5975d6bSopenharmony_ci test_normal_checking_tuple_offsets_minimal_sized); 150b5975d6bSopenharmony_ci g_test_add_func ("/gvariant/normal-checking/empty-object-path", 151b5975d6bSopenharmony_ci-- 152b5975d6bSopenharmony_ciGitLab 153b5975d6bSopenharmony_ci 154b5975d6bSopenharmony_ci 155b5975d6bSopenharmony_ciFrom 21a204147b16539b3eda3143b32844c49e29f4d4 Mon Sep 17 00:00:00 2001 156b5975d6bSopenharmony_ciFrom: Philip Withnall <pwithnall@endlessos.org> 157b5975d6bSopenharmony_ciDate: Thu, 15 Dec 2022 16:49:28 +0000 158b5975d6bSopenharmony_ciSubject: [PATCH 2/2] gvariant: Propagate trust when getting a child of a 159b5975d6bSopenharmony_ci serialised variant 160b5975d6bSopenharmony_ci 161b5975d6bSopenharmony_ciIf a variant is trusted, that means all its children are trusted, so 162b5975d6bSopenharmony_ciensure that their checked offsets are set as such. 163b5975d6bSopenharmony_ci 164b5975d6bSopenharmony_ciThis allows a lot of the offset table checks to be avoided when getting 165b5975d6bSopenharmony_cichildren from trusted serialised tuples, which speeds things up. 166b5975d6bSopenharmony_ci 167b5975d6bSopenharmony_ciNo unit test is included because this is just a performance fix. If 168b5975d6bSopenharmony_cithere are other slownesses, or regressions, in serialised `GVariant` 169b5975d6bSopenharmony_ciperformance, the fuzzing setup will catch them like it did this one. 170b5975d6bSopenharmony_ci 171b5975d6bSopenharmony_ciThis change does reduce the time to run the oss-fuzz reproducer from 80s 172b5975d6bSopenharmony_cito about 0.7s on my machine. 173b5975d6bSopenharmony_ci 174b5975d6bSopenharmony_ciSigned-off-by: Philip Withnall <pwithnall@endlessos.org> 175b5975d6bSopenharmony_ci 176b5975d6bSopenharmony_ciFixes: #2841 177b5975d6bSopenharmony_cioss-fuzz#54314 178b5975d6bSopenharmony_ci--- 179b5975d6bSopenharmony_ci glib/gvariant-core.c | 4 ++-- 180b5975d6bSopenharmony_ci 1 file changed, 2 insertions(+), 2 deletions(-) 181b5975d6bSopenharmony_ci 182b5975d6bSopenharmony_cidiff --git a/glib/gvariant-core.c b/glib/gvariant-core.c 183b5975d6bSopenharmony_ciindex f441c4757e..4778022829 100644 184b5975d6bSopenharmony_ci--- a/glib/gvariant-core.c 185b5975d6bSopenharmony_ci+++ b/glib/gvariant-core.c 186b5975d6bSopenharmony_ci@@ -1198,8 +1198,8 @@ g_variant_get_child_value (GVariant *value, 187b5975d6bSopenharmony_ci child->contents.serialised.bytes = 188b5975d6bSopenharmony_ci g_bytes_ref (value->contents.serialised.bytes); 189b5975d6bSopenharmony_ci child->contents.serialised.data = s_child.data; 190b5975d6bSopenharmony_ci- child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to; 191b5975d6bSopenharmony_ci- child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to; 192b5975d6bSopenharmony_ci+ child->contents.serialised.ordered_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.ordered_offsets_up_to; 193b5975d6bSopenharmony_ci+ child->contents.serialised.checked_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.checked_offsets_up_to; 194b5975d6bSopenharmony_ci 195b5975d6bSopenharmony_ci return child; 196b5975d6bSopenharmony_ci } 197b5975d6bSopenharmony_ci-- 198b5975d6bSopenharmony_ciGitLab 199b5975d6bSopenharmony_ci 200