6fcf6b
From 89b522ed31837cb2ac107a8961fbb0f2c7fc7ccb Mon Sep 17 00:00:00 2001
6fcf6b
From: Krzesimir Nowak <qdlacz@gmail.com>
6fcf6b
Date: Wed, 10 Feb 2021 23:51:07 +0100
6fcf6b
Subject: [PATCH] gbytearray: Do not accept too large byte arrays
6fcf6b
6fcf6b
GByteArray uses guint for storing the length of the byte array, but it
6fcf6b
also has a constructor (g_byte_array_new_take) that takes length as a
6fcf6b
gsize. gsize may be larger than guint (64 bits for gsize vs 32 bits
6fcf6b
for guint). It is possible to call the function with a value greater
6fcf6b
than G_MAXUINT, which will result in silent length truncation. This
6fcf6b
may happen as a result of unreffing GBytes into GByteArray, so rather
6fcf6b
be loud about it.
6fcf6b
6fcf6b
(Test case tweaked by Philip Withnall.)
6fcf6b
---
6fcf6b
 glib/garray.c      |  6 ++++++
6fcf6b
 glib/gbytes.c      |  4 ++++
6fcf6b
 glib/tests/bytes.c | 37 +++++++++++++++++++++++++++++++++++--
6fcf6b
 3 files changed, 45 insertions(+), 2 deletions(-)
6fcf6b
6fcf6b
diff --git a/glib/garray.c b/glib/garray.c
6fcf6b
index aa3c04707..271d85ad8 100644
6fcf6b
--- a/glib/garray.c
6fcf6b
+++ b/glib/garray.c
6fcf6b
@@ -1666,6 +1666,10 @@ g_byte_array_new (void)
6fcf6b
  * Create byte array containing the data. The data will be owned by the array
6fcf6b
  * and will be freed with g_free(), i.e. it could be allocated using g_strdup().
6fcf6b
  *
6fcf6b
+ * Do not use it if @len is greater than %G_MAXUINT. #GByteArray
6fcf6b
+ * stores the length of its data in #guint, which may be shorter than
6fcf6b
+ * #gsize.
6fcf6b
+ *
6fcf6b
  * Since: 2.32
6fcf6b
  *
6fcf6b
  * Returns: (transfer full): a new #GByteArray
6fcf6b
@@ -1677,6 +1681,8 @@ g_byte_array_new_take (guint8 *data,
6fcf6b
   GByteArray *array;
6fcf6b
   GRealArray *real;
6fcf6b
 
6fcf6b
+  g_return_val_if_fail (len <= G_MAXUINT, NULL);
6fcf6b
+
6fcf6b
   array = g_byte_array_new ();
6fcf6b
   real = (GRealArray *)array;
6fcf6b
   g_assert (real->data == NULL);
6fcf6b
diff --git a/glib/gbytes.c b/glib/gbytes.c
6fcf6b
index 5141170d7..635b79535 100644
6fcf6b
--- a/glib/gbytes.c
6fcf6b
+++ b/glib/gbytes.c
6fcf6b
@@ -512,6 +512,10 @@ g_bytes_unref_to_data (GBytes *bytes,
6fcf6b
  * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes(). In all
6fcf6b
  * other cases the data is copied.
6fcf6b
  *
6fcf6b
+ * Do not use it if @bytes contains more than %G_MAXUINT
6fcf6b
+ * bytes. #GByteArray stores the length of its data in #guint, which
6fcf6b
+ * may be shorter than #gsize, that @bytes is using.
6fcf6b
+ *
6fcf6b
  * Returns: (transfer full): a new mutable #GByteArray containing the same byte data
6fcf6b
  *
6fcf6b
  * Since: 2.32
6fcf6b
diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c
6fcf6b
index 5ea5c2b35..42281307b 100644
6fcf6b
--- a/glib/tests/bytes.c
6fcf6b
+++ b/glib/tests/bytes.c
6fcf6b
@@ -10,12 +10,12 @@
6fcf6b
  */
6fcf6b
 
6fcf6b
 #undef G_DISABLE_ASSERT
6fcf6b
-#undef G_LOG_DOMAIN
6fcf6b
 
6fcf6b
 #include <stdio.h>
6fcf6b
 #include <stdlib.h>
6fcf6b
 #include <string.h>
6fcf6b
 #include "glib.h"
6fcf6b
+#include "glib/gstrfuncsprivate.h"
6fcf6b
 
6fcf6b
 /* Keep in sync with glib/gbytes.c */
6fcf6b
 struct _GBytes
6fcf6b
@@ -333,6 +333,38 @@ test_to_array_transferred (void)
6fcf6b
   g_byte_array_unref (array);
6fcf6b
 }
6fcf6b
 
6fcf6b
+static void
6fcf6b
+test_to_array_transferred_oversize (void)
6fcf6b
+{
6fcf6b
+  g_test_message ("g_bytes_unref_to_array() can only take GBytes up to "
6fcf6b
+                  "G_MAXUINT in length; test that longer ones are rejected");
6fcf6b
+
6fcf6b
+  if (sizeof (guint) >= sizeof (gsize))
6fcf6b
+    {
6fcf6b
+      g_test_skip ("Skipping test as guint is not smaller than gsize");
6fcf6b
+    }
6fcf6b
+  else if (g_test_undefined ())
6fcf6b
+    {
6fcf6b
+      GByteArray *array = NULL;
6fcf6b
+      GBytes *bytes = NULL;
6fcf6b
+      gpointer data = g_memdup2 (NYAN, N_NYAN);
6fcf6b
+      gsize len = ((gsize) G_MAXUINT) + 1;
6fcf6b
+
6fcf6b
+      bytes = g_bytes_new_take (data, len);
6fcf6b
+      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
6fcf6b
+                             "g_byte_array_new_take: assertion 'len <= G_MAXUINT' failed");
6fcf6b
+      array = g_bytes_unref_to_array (g_steal_pointer (&bytes));
6fcf6b
+      g_test_assert_expected_messages ();
6fcf6b
+      g_assert_null (array);
6fcf6b
+
6fcf6b
+      g_free (data);
6fcf6b
+    }
6fcf6b
+  else
6fcf6b
+    {
6fcf6b
+      g_test_skip ("Skipping test as testing undefined behaviour is disabled");
6fcf6b
+    }
6fcf6b
+}
6fcf6b
+
6fcf6b
 static void
6fcf6b
 test_to_array_two_refs (void)
6fcf6b
 {
6fcf6b
@@ -407,7 +439,8 @@ main (int argc, char *argv[])
6fcf6b
   g_test_add_func ("/bytes/to-data/transfered", test_to_data_transferred);
6fcf6b
   g_test_add_func ("/bytes/to-data/two-refs", test_to_data_two_refs);
6fcf6b
   g_test_add_func ("/bytes/to-data/non-malloc", test_to_data_non_malloc);
6fcf6b
-  g_test_add_func ("/bytes/to-array/transfered", test_to_array_transferred);
6fcf6b
+  g_test_add_func ("/bytes/to-array/transferred", test_to_array_transferred);
6fcf6b
+  g_test_add_func ("/bytes/to-array/transferred-oversize", test_to_array_transferred_oversize);
6fcf6b
   g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs);
6fcf6b
   g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc);
6fcf6b
   g_test_add_func ("/bytes/null", test_null);
6fcf6b
-- 
6fcf6b
2.31.1
6fcf6b