Commit | Line | Data |
---|---|---|
21b3b755 MW |
1 | Backport of: |
2 | ||
3 | From 0f384c88a241bbbd884487b1c40b7b75f1e638d3 Mon Sep 17 00:00:00 2001 | |
4 | From: Krzesimir Nowak <qdlacz@gmail.com> | |
5 | Date: Wed, 10 Feb 2021 23:51:07 +0100 | |
6 | Subject: [PATCH] gbytearray: Do not accept too large byte arrays | |
7 | ||
8 | GByteArray uses guint for storing the length of the byte array, but it | |
9 | also has a constructor (g_byte_array_new_take) that takes length as a | |
10 | gsize. gsize may be larger than guint (64 bits for gsize vs 32 bits | |
11 | for guint). It is possible to call the function with a value greater | |
12 | than G_MAXUINT, which will result in silent length truncation. This | |
13 | may happen as a result of unreffing GBytes into GByteArray, so rather | |
14 | be loud about it. | |
15 | ||
16 | (Test case tweaked by Philip Withnall.) | |
17 | ||
18 | (Backport 2.66: Add #include gstrfuncsprivate.h in the test case for | |
19 | `g_memdup2()`.) | |
20 | --- | |
21 | glib/garray.c | 6 ++++++ | |
22 | glib/gbytes.c | 4 ++++ | |
23 | glib/tests/bytes.c | 35 ++++++++++++++++++++++++++++++++++- | |
24 | 3 files changed, 44 insertions(+), 1 deletion(-) | |
25 | ||
26 | diff --git a/glib/garray.c b/glib/garray.c | |
27 | index 942e74c9f..fb1a42aaf 100644 | |
28 | --- a/glib/garray.c | |
29 | +++ b/glib/garray.c | |
30 | @@ -2013,6 +2013,10 @@ g_byte_array_new (void) | |
31 | * Create byte array containing the data. The data will be owned by the array | |
32 | * and will be freed with g_free(), i.e. it could be allocated using g_strdup(). | |
33 | * | |
34 | + * Do not use it if @len is greater than %G_MAXUINT. #GByteArray | |
35 | + * stores the length of its data in #guint, which may be shorter than | |
36 | + * #gsize. | |
37 | + * | |
38 | * Since: 2.32 | |
39 | * | |
40 | * Returns: (transfer full): a new #GByteArray | |
41 | @@ -2024,6 +2028,8 @@ g_byte_array_new_take (guint8 *data, | |
42 | GByteArray *array; | |
43 | GRealArray *real; | |
44 | ||
45 | + g_return_val_if_fail (len <= G_MAXUINT, NULL); | |
46 | + | |
47 | array = g_byte_array_new (); | |
48 | real = (GRealArray *)array; | |
49 | g_assert (real->data == NULL); | |
50 | diff --git a/glib/gbytes.c b/glib/gbytes.c | |
51 | index 7b72886e5..d56abe6c3 100644 | |
52 | --- a/glib/gbytes.c | |
53 | +++ b/glib/gbytes.c | |
54 | @@ -519,6 +519,10 @@ g_bytes_unref_to_data (GBytes *bytes, | |
55 | * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes(). In all | |
56 | * other cases the data is copied. | |
57 | * | |
58 | + * Do not use it if @bytes contains more than %G_MAXUINT | |
59 | + * bytes. #GByteArray stores the length of its data in #guint, which | |
60 | + * may be shorter than #gsize, that @bytes is using. | |
61 | + * | |
62 | * Returns: (transfer full): a new mutable #GByteArray containing the same byte data | |
63 | * | |
64 | * Since: 2.32 | |
65 | diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c | |
66 | index 5ea5c2b35..15a6aaad6 100644 | |
67 | --- a/glib/tests/bytes.c | |
68 | +++ b/glib/tests/bytes.c | |
69 | @@ -10,12 +10,12 @@ | |
70 | */ | |
71 | ||
72 | #undef G_DISABLE_ASSERT | |
73 | -#undef G_LOG_DOMAIN | |
74 | ||
75 | #include <stdio.h> | |
76 | #include <stdlib.h> | |
77 | #include <string.h> | |
78 | #include "glib.h" | |
79 | +#include "glib/gstrfuncsprivate.h" | |
80 | ||
81 | /* Keep in sync with glib/gbytes.c */ | |
82 | struct _GBytes | |
83 | @@ -333,6 +333,38 @@ test_to_array_transferred (void) | |
84 | g_byte_array_unref (array); | |
85 | } | |
86 | ||
87 | +static void | |
88 | +test_to_array_transferred_oversize (void) | |
89 | +{ | |
90 | + g_test_message ("g_bytes_unref_to_array() can only take GBytes up to " | |
91 | + "G_MAXUINT in length; test that longer ones are rejected"); | |
92 | + | |
93 | + if (sizeof (guint) >= sizeof (gsize)) | |
94 | + { | |
95 | + g_test_skip ("Skipping test as guint is not smaller than gsize"); | |
96 | + } | |
97 | + else if (g_test_undefined ()) | |
98 | + { | |
99 | + GByteArray *array = NULL; | |
100 | + GBytes *bytes = NULL; | |
101 | + gpointer data = g_memdup2 (NYAN, N_NYAN); | |
102 | + gsize len = ((gsize) G_MAXUINT) + 1; | |
103 | + | |
104 | + bytes = g_bytes_new_take (data, len); | |
105 | + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, | |
106 | + "g_byte_array_new_take: assertion 'len <= G_MAXUINT' failed"); | |
107 | + array = g_bytes_unref_to_array (g_steal_pointer (&bytes)); | |
108 | + g_test_assert_expected_messages (); | |
109 | + g_assert_null (array); | |
110 | + | |
111 | + g_free (data); | |
112 | + } | |
113 | + else | |
114 | + { | |
115 | + g_test_skip ("Skipping test as testing undefined behaviour is disabled"); | |
116 | + } | |
117 | +} | |
118 | + | |
119 | static void | |
120 | test_to_array_two_refs (void) | |
121 | { | |
122 | @@ -410,6 +442,7 @@ main (int argc, char *argv[]) | |
123 | g_test_add_func ("/bytes/to-array/transfered", test_to_array_transferred); | |
124 | g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs); | |
125 | g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc); | |
126 | + g_test_add_func ("/bytes/to-array/transferred/oversize", test_to_array_transferred_oversize); | |
127 | g_test_add_func ("/bytes/null", test_null); | |
128 | ||
129 | return g_test_run (); | |
130 | -- | |
131 | 2.30.1 | |
132 |