Package: glib2.0 / 2.58.3-2+deb10u3

gbytearray-Do-not-accept-too-large-byte-arrays.patch Patch series | download
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
From: Krzesimir Nowak <qdlacz@gmail.com>
Date: Wed, 10 Feb 2021 23:51:07 +0100
Subject: gbytearray: Do not accept too large byte arrays

GByteArray uses guint for storing the length of the byte array, but it
also has a constructor (g_byte_array_new_take) that takes length as a
gsize. gsize may be larger than guint (64 bits for gsize vs 32 bits
for guint). It is possible to call the function with a value greater
than G_MAXUINT, which will result in silent length truncation. This
may happen as a result of unreffing GBytes into GByteArray, so rather
be loud about it.

(Test case tweaked by Philip Withnall.)

(Backport 2.66: Add #include gstrfuncsprivate.h in the test case for
`g_memdup2()`.)

Fixes: CVE-2021-27218
Bug-Debian: https://bugs.debian.org/982779
Origin: backport, commit:0f384c88a241bbbd884487b1c40b7b75f1e638d3
Forwarded: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2001
---
 glib/garray.c      |  6 ++++++
 glib/gbytes.c      |  4 ++++
 glib/tests/bytes.c | 37 +++++++++++++++++++++++++++++++++++--
 3 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/glib/garray.c b/glib/garray.c
index a6cbd57..b00033a 100644
--- a/glib/garray.c
+++ b/glib/garray.c
@@ -1755,6 +1755,10 @@ g_byte_array_new (void)
  * Create byte array containing the data. The data will be owned by the array
  * and will be freed with g_free(), i.e. it could be allocated using g_strdup().
  *
+ * Do not use it if @len is greater than %G_MAXUINT. #GByteArray
+ * stores the length of its data in #guint, which may be shorter than
+ * #gsize.
+ *
  * Since: 2.32
  *
  * Returns: (transfer full): a new #GByteArray
@@ -1766,6 +1770,8 @@ g_byte_array_new_take (guint8 *data,
   GByteArray *array;
   GRealArray *real;
 
+  g_return_val_if_fail (len <= G_MAXUINT, NULL);
+
   array = g_byte_array_new ();
   real = (GRealArray *)array;
   g_assert (real->data == NULL);
diff --git a/glib/gbytes.c b/glib/gbytes.c
index 84c87e4..dee4948 100644
--- a/glib/gbytes.c
+++ b/glib/gbytes.c
@@ -521,6 +521,10 @@ g_bytes_unref_to_data (GBytes *bytes,
  * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes(). In all
  * other cases the data is copied.
  *
+ * Do not use it if @bytes contains more than %G_MAXUINT
+ * bytes. #GByteArray stores the length of its data in #guint, which
+ * may be shorter than #gsize, that @bytes is using.
+ *
  * Returns: (transfer full): a new mutable #GByteArray containing the same byte data
  *
  * Since: 2.32
diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c
index 5ea5c2b..9e26382 100644
--- a/glib/tests/bytes.c
+++ b/glib/tests/bytes.c
@@ -10,12 +10,12 @@
  */
 
 #undef G_DISABLE_ASSERT
-#undef G_LOG_DOMAIN
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "glib.h"
+#include "glib/gstrfuncsprivate.h"
 
 /* Keep in sync with glib/gbytes.c */
 struct _GBytes
@@ -333,6 +333,38 @@ test_to_array_transferred (void)
   g_byte_array_unref (array);
 }
 
+static void
+test_to_array_transferred_oversize (void)
+{
+  g_test_message ("g_bytes_unref_to_array() can only take GBytes up to "
+                  "G_MAXUINT in length; test that longer ones are rejected");
+
+  if (sizeof (guint) >= sizeof (gsize))
+    {
+      g_test_skip ("Skipping test as guint is not smaller than gsize");
+    }
+  else if (g_test_undefined ())
+    {
+      GByteArray *array = NULL;
+      GBytes *bytes = NULL;
+      gpointer data = g_memdup2 (NYAN, N_NYAN);
+      gsize len = ((gsize) G_MAXUINT) + 1;
+
+      bytes = g_bytes_new_take (data, len);
+      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                             "g_byte_array_new_take: assertion 'len <= G_MAXUINT' failed");
+      array = g_bytes_unref_to_array (g_steal_pointer (&bytes));
+      g_test_assert_expected_messages ();
+      g_assert_null (array);
+
+      g_free (data);
+    }
+  else
+    {
+      g_test_skip ("Skipping test as testing undefined behaviour is disabled");
+    }
+}
+
 static void
 test_to_array_two_refs (void)
 {
@@ -407,7 +439,8 @@ main (int argc, char *argv[])
   g_test_add_func ("/bytes/to-data/transfered", test_to_data_transferred);
   g_test_add_func ("/bytes/to-data/two-refs", test_to_data_two_refs);
   g_test_add_func ("/bytes/to-data/non-malloc", test_to_data_non_malloc);
-  g_test_add_func ("/bytes/to-array/transfered", test_to_array_transferred);
+  g_test_add_func ("/bytes/to-array/transferred", test_to_array_transferred);
+  g_test_add_func ("/bytes/to-array/transferred/oversize", test_to_array_transferred_oversize);
   g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs);
   g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc);
   g_test_add_func ("/bytes/null", test_null);