summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32643.patch
blob: 9c0867bf5f1addda1b7379e16c47f57ee0d20250 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
From 78da5faccb3e065116b75b3ff87ff55381da6c76 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 17 Aug 2023 11:24:43 +0000
Subject: [PATCH] gvariant: Check offset table doesn't fall outside variant
 bounds

When dereferencing the first entry in the offset table for a tuple,
check that it doesn’t fall outside the bounds of the variant first.

This prevents an out-of-bounds read from some non-normal tuples.

This bug was introduced in commit 73d0aa81c2575a5c9ae77d.

Includes a unit test, although the test will likely only catch the
original bug if run with asan enabled.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Fixes: #2840
oss-fuzz#54302

CVE: CVE-2023-32643
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/78da5faccb3e065116b75b3ff87ff55381da6c76]
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
---
 glib/gvariant-serialiser.c | 12 ++++++--
 glib/tests/gvariant.c      | 63 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
index 5aa2cbc..4e50ed7 100644
--- a/glib/gvariant-serialiser.c
+++ b/glib/gvariant-serialiser.c
@@ -979,7 +979,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
 
   member_info = g_variant_type_info_member_info (value.type_info, index_);
 
-  if (member_info->i + 1)
+  if (member_info->i + 1 &&
+      offset_size * (member_info->i + 1) <= value.size)
     member_start = gvs_read_unaligned_le (value.data + value.size -
                                           offset_size * (member_info->i + 1),
                                           offset_size);
@@ -990,7 +991,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
   member_start &= member_info->b;
   member_start |= member_info->c;
 
-  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
+  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST &&
+      offset_size * (member_info->i + 1) <= value.size)
     member_end = value.size - offset_size * (member_info->i + 1);
 
   else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
@@ -1001,11 +1003,15 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
       member_end = member_start + fixed_size;
     }
 
-  else /* G_VARIANT_MEMBER_ENDING_OFFSET */
+  else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET &&
+           offset_size * (member_info->i + 2) <= value.size)
     member_end = gvs_read_unaligned_le (value.data + value.size -
                                         offset_size * (member_info->i + 2),
                                         offset_size);
 
+  else  /* invalid */
+    member_end = G_MAXSIZE;
+
   if (out_member_start != NULL)
     *out_member_start = member_start;
   if (out_member_end != NULL)
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
index 679dd40..2eca8be 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -5432,6 +5432,67 @@ test_normal_checking_tuple_offsets4 (void)
   g_variant_unref (variant);
 }
 
+/* This is a regression test that dereferencing the first element in the offset
+ * table doesn’t dereference memory before the start of the GVariant. The first
+ * element in the offset table gives the offset of the final member in the
+ * tuple (the offset table is stored in reverse), and the position of this final
+ * member is needed to check that none of the tuple members overlap with the
+ * offset table
+ *
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2840 */
+static void
+test_normal_checking_tuple_offsets5 (void)
+{
+  /* A tuple of type (sss) in normal form would have an offset table with two
+   * entries:
+   *  - The first entry (lowest index in the table) gives the offset of the
+   *    third `s` in the tuple, as the offset table is reversed compared to the
+   *    tuple members.
+   *  - The second entry (highest index in the table) gives the offset of the
+   *    second `s` in the tuple.
+   *  - The offset of the first `s` in the tuple is always 0.
+   *
+   * See §2.5.4 (Structures) of the GVariant specification for details, noting
+   * that the table is only layed out this way because all three members of the
+   * tuple have non-fixed sizes.
+   *
+   * It’s not clear whether the 0xaa data of this variant is part of the strings
+   * in the tuple, or part of the offset table. It doesn’t really matter. This
+   * is a regression test to check that the code to validate the offset table
+   * doesn’t unconditionally try to access the first entry in the offset table
+   * by subtracting the table size from the end of the GVariant data.
+   *
+   * In this non-normal case, that would result in an address off the start of
+   * the GVariant data, and an out-of-bounds read, because the GVariant is one
+   * byte long, but the offset table is calculated as two bytes long (with 1B
+   * sized entries) from the tuple’s type.
+   */
+  const GVariantType *data_type = G_VARIANT_TYPE ("(sss)");
+  const guint8 data[] = { 0xaa };
+  gsize size = sizeof (data);
+  GVariant *variant = NULL;
+  GVariant *normal_variant = NULL;
+  GVariant *expected = NULL;
+
+  g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2840");
+
+  variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
+  g_assert_nonnull (variant);
+
+  g_assert_false (g_variant_is_normal_form (variant));
+
+  normal_variant = g_variant_get_normal_form (variant);
+  g_assert_nonnull (normal_variant);
+
+  expected = g_variant_new_parsed ("('', '', '')");
+  g_assert_cmpvariant (expected, variant);
+  g_assert_cmpvariant (expected, normal_variant);
+
+  g_variant_unref (expected);
+  g_variant_unref (normal_variant);
+  g_variant_unref (variant);
+}
+
 /* Test that an otherwise-valid serialised GVariant is considered non-normal if
  * its offset table entries are too wide.
  *
@@ -5680,6 +5741,8 @@ main (int argc, char **argv)
                    test_normal_checking_tuple_offsets3);
   g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
                    test_normal_checking_tuple_offsets4);
+  g_test_add_func ("/gvariant/normal-checking/tuple-offsets5",
+                   test_normal_checking_tuple_offsets5);
   g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized",
                    test_normal_checking_tuple_offsets_minimal_sized);
   g_test_add_func ("/gvariant/normal-checking/empty-object-path",
-- 
2.24.4