135 lines
5.2 KiB
Diff
135 lines
5.2 KiB
Diff
From 0f2a6c61c9c5e34d57293fb6987b21f3d1feb1cb Mon Sep 17 00:00:00 2001
|
||
From: Philip Withnall <withnall@endlessm.com>
|
||
Date: Tue, 13 Feb 2018 13:29:23 +0000
|
||
Subject: [PATCH 232/682] =?UTF-8?q?gvariant:=20Realign=20data=20on=20const?=
|
||
=?UTF-8?q?ruction=20if=20it=E2=80=99s=20not=20properly=20aligned?=
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
Otherwise the GVariant would later fail internal alignment checks,
|
||
aborting the program.
|
||
|
||
If unaligned data is provided to (for example)
|
||
g_variant_new_from_data(), it will copy the data into a new aligned
|
||
allocation. This is slow, but better than crashing. If callers want
|
||
better performance, they should provide aligned data in their call, and
|
||
it will not be copied or reallocated.
|
||
|
||
Includes a unit test.
|
||
|
||
Signed-off-by: Philip Withnall <withnall@endlessm.com>
|
||
|
||
https://gitlab.gnome.org/GNOME/glib/issues/1342
|
||
---
|
||
glib/gvariant-core.c | 46 +++++++++++++++++++++++++++++++++++++++--
|
||
glib/gvariant.c | 10 +++++++++
|
||
glib/tests/gvariant.c | 48 +++++++++++++++++++++++++++++++++++++++++++
|
||
3 files changed, 102 insertions(+), 2 deletions(-)
|
||
|
||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||
index 815bdf9e0..ef59c7049 100644
|
||
--- a/glib/gvariant-core.c
|
||
+++ b/glib/gvariant-core.c
|
||
@@ -506,6 +506,10 @@ g_variant_alloc (const GVariantType *type,
|
||
*
|
||
* A reference is taken on @bytes.
|
||
*
|
||
+ * The data in @bytes must be aligned appropriately for the @type being loaded.
|
||
+ * Otherwise this function will internally create a copy of the memory (since
|
||
+ * GLib 2.60) or (in older versions) fail and exit the process.
|
||
+ *
|
||
* Returns: (transfer none): a new #GVariant with a floating reference
|
||
*
|
||
* Since: 2.36
|
||
@@ -518,14 +522,50 @@ g_variant_new_from_bytes (const GVariantType *type,
|
||
GVariant *value;
|
||
guint alignment;
|
||
gsize size;
|
||
+ GBytes *owned_bytes = NULL;
|
||
|
||
value = g_variant_alloc (type, TRUE, trusted);
|
||
|
||
- value->contents.serialised.bytes = g_bytes_ref (bytes);
|
||
-
|
||
g_variant_type_info_query (value->type_info,
|
||
&alignment, &size);
|
||
|
||
+ /* Ensure the alignment is correct. This is a huge performance hit if it’s
|
||
+ * not correct, but that’s better than aborting if a caller provides data
|
||
+ * with the wrong alignment (which is likely to happen very occasionally, and
|
||
+ * only cause an abort on some architectures — so is unlikely to be caught
|
||
+ * in testing). Callers can always actively ensure they use the correct
|
||
+ * alignment to avoid the performance hit. */
|
||
+ if ((alignment & (gsize) g_bytes_get_data (bytes, NULL)) != 0)
|
||
+ {
|
||
+#ifdef HAVE_POSIX_MEMALIGN
|
||
+ gpointer aligned_data = NULL;
|
||
+ gsize aligned_size = g_bytes_get_size (bytes);
|
||
+
|
||
+ /* posix_memalign() requires the alignment to be a multiple of
|
||
+ * sizeof(void*), and a power of 2. See g_variant_type_info_query() for
|
||
+ * details on the alignment format. */
|
||
+ if (posix_memalign (&aligned_data, MAX (sizeof (void *), alignment + 1),
|
||
+ aligned_size) != 0)
|
||
+ g_error ("posix_memalign failed");
|
||
+
|
||
+ memcpy (aligned_data, g_bytes_get_data (bytes, NULL), aligned_size);
|
||
+
|
||
+ bytes = owned_bytes = g_bytes_new_with_free_func (aligned_data,
|
||
+ aligned_size,
|
||
+ free, aligned_data);
|
||
+ aligned_data = NULL;
|
||
+#else
|
||
+ /* NOTE: there may be platforms that lack posix_memalign() and also
|
||
+ * have malloc() that returns non-8-aligned. if so, we need to try
|
||
+ * harder here.
|
||
+ */
|
||
+ bytes = owned_bytes = g_bytes_new (g_bytes_get_data (bytes, NULL),
|
||
+ g_bytes_get_size (bytes));
|
||
+#endif
|
||
+ }
|
||
+
|
||
+ value->contents.serialised.bytes = g_bytes_ref (bytes);
|
||
+
|
||
if (size && g_bytes_get_size (bytes) != size)
|
||
{
|
||
/* Creating a fixed-sized GVariant with a bytes of the wrong
|
||
@@ -543,6 +583,8 @@ g_variant_new_from_bytes (const GVariantType *type,
|
||
value->contents.serialised.data = g_bytes_get_data (bytes, &value->size);
|
||
}
|
||
|
||
+ g_clear_pointer (&owned_bytes, g_bytes_unref);
|
||
+
|
||
return value;
|
||
}
|
||
|
||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||
index d45b487ad..983d4704c 100644
|
||
--- a/glib/gvariant.c
|
||
+++ b/glib/gvariant.c
|
||
@@ -307,6 +307,11 @@
|
||
* Constructs a new trusted #GVariant instance from the provided data.
|
||
* This is used to implement g_variant_new_* for all the basic types.
|
||
*
|
||
+ * Note: @data must be backed by memory that is aligned appropriately for the
|
||
+ * @type being loaded. Otherwise this function will internally create a copy of
|
||
+ * the memory (since GLib 2.60) or (in older versions) fail and exit the
|
||
+ * process.
|
||
+ *
|
||
* Returns: a new floating #GVariant
|
||
*/
|
||
static GVariant *
|
||
@@ -5986,6 +5991,11 @@ g_variant_byteswap (GVariant *value)
|
||
* needed. The exact time of this call is unspecified and might even be
|
||
* before this function returns.
|
||
*
|
||
+ * Note: @data must be backed by memory that is aligned appropriately for the
|
||
+ * @type being loaded. Otherwise this function will internally create a copy of
|
||
+ * the memory (since GLib 2.60) or (in older versions) fail and exit the
|
||
+ * process.
|
||
+ *
|
||
* Returns: (transfer none): a new floating #GVariant of type @type
|
||
*
|
||
* Since: 2.24
|