* @returns: the serialised form of @value, or %NULL
*
* Returns a pointer to the serialised form of a #GVariant instance.
- * The returned data is in machine native byte order but may not be in
- * fully-normalised form if read from an untrusted source. The returned
- * data must not be freed; it remains valid for as long as @value
- * exists.
+ * The returned data may not be in fully-normalised form if read from an
+ * untrusted source. The returned data must not be freed; it remains
+ * valid for as long as @value exists.
*
* If @value is a fixed-sized value that was deserialised from a
* corrupted serialised container then %NULL may be returned. In this
g_variant_unlock (value);
}
+/**
+ * g_variant_is_normal_form:
+ * @value: a #GVariant instance
+ * @returns: %TRUE if @value is in normal form
+ *
+ * Checks if @value is in normal form.
+ *
+ * The main reason to do this is to detect if a given chunk of
+ * serialised data is in normal form: load the data into a #GVariant
+ * using g_variant_create_from_data() and then use this function to
+ * check.
+ *
+ * If @value is found to be in normal form then it will be marked as
+ * being trusted. If the value was already marked as being trusted then
+ * this function will immediately return %TRUE.
+ *
+ * Since: 2.24
+ **/
+gboolean
+g_variant_is_normal_form (GVariant *value)
+{
+ if (value->state & STATE_TRUSTED)
+ return TRUE;
+
+ g_variant_lock (value);
+
+ if (value->state & STATE_SERIALISED)
+ {
+ GVariantSerialised serialised = {
+ value->type_info,
+ (gpointer) value->contents.serialised.data,
+ value->size
+ };
+
+ if (g_variant_serialised_is_normal (serialised))
+ value->state |= STATE_TRUSTED;
+ }
+ else
+ {
+ gboolean normal = TRUE;
+ gsize i;
+
+ for (i = 0; i < value->contents.tree.n_children; i++)
+ normal &= g_variant_is_normal_form (value->contents.tree.children[i]);
+
+ if (normal)
+ value->state |= STATE_TRUSTED;
+ }
+
+ g_variant_unlock (value);
+
+ return (value->state & STATE_TRUSTED) != 0;
+}
+
#define __G_VARIANT_CORE_C__
#include "galiasdef.c"
return value != NULL;
}
+/* Serialised data {{{1 */
+static GVariant *
+g_variant_deep_copy (GVariant *value)
+{
+ switch (g_variant_classify (value))
+ {
+ case G_VARIANT_CLASS_MAYBE:
+ case G_VARIANT_CLASS_ARRAY:
+ case G_VARIANT_CLASS_TUPLE:
+ case G_VARIANT_CLASS_DICT_ENTRY:
+ case G_VARIANT_CLASS_VARIANT:
+ {
+ GVariantBuilder builder;
+ GVariantIter iter;
+ GVariant *child;
+
+ g_variant_builder_init (&builder, g_variant_get_type (value));
+ g_variant_iter_init (&iter, value);
+
+ while ((child = g_variant_iter_next_value (&iter)))
+ {
+ g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
+ g_variant_unref (child);
+ }
+
+ return g_variant_builder_end (&builder);
+ }
+
+ case G_VARIANT_CLASS_BOOLEAN:
+ return g_variant_new_boolean (g_variant_get_boolean (value));
+
+ case G_VARIANT_CLASS_BYTE:
+ return g_variant_new_byte (g_variant_get_byte (value));
+
+ case G_VARIANT_CLASS_INT16:
+ return g_variant_new_int16 (g_variant_get_int16 (value));
+
+ case G_VARIANT_CLASS_UINT16:
+ return g_variant_new_uint16 (g_variant_get_uint16 (value));
+
+ case G_VARIANT_CLASS_INT32:
+ return g_variant_new_int32 (g_variant_get_int32 (value));
+
+ case G_VARIANT_CLASS_UINT32:
+ return g_variant_new_uint32 (g_variant_get_uint32 (value));
+
+ case G_VARIANT_CLASS_INT64:
+ return g_variant_new_int64 (g_variant_get_int64 (value));
+
+ case G_VARIANT_CLASS_UINT64:
+ return g_variant_new_uint64 (g_variant_get_uint64 (value));
+
+ case G_VARIANT_CLASS_HANDLE:
+ return g_variant_new_handle (g_variant_get_handle (value));
+
+ case G_VARIANT_CLASS_DOUBLE:
+ return g_variant_new_double (g_variant_get_double (value));
+
+ case G_VARIANT_CLASS_STRING:
+ return g_variant_new_string (g_variant_get_string (value, NULL));
+
+ case G_VARIANT_CLASS_OBJECT_PATH:
+ return g_variant_new_object_path (g_variant_get_string (value, NULL));
+
+ case G_VARIANT_CLASS_SIGNATURE:
+ return g_variant_new_signature (g_variant_get_string (value, NULL));
+ }
+
+ g_assert_not_reached ();
+}
+
+/**
+ * g_variant_get_normal_form:
+ * @value: a #GVariant
+ * @returns: a trusted #GVariant
+ *
+ * Gets a #GVariant instance that has the same value as @value and is
+ * trusted to be in normal form.
+ *
+ * If @value is already trusted to be in normal form then a new
+ * reference to @value is returned.
+ *
+ * If @value is not already trusted, then it is scanned to check if it
+ * is in normal form. If it is found to be in normal form then it is
+ * marked as trusted and a new reference to it is returned.
+ *
+ * If @value is found not to be in normal form then a new trusted
+ * #GVariant is created with the same value as @value.
+ *
+ * It makes sense to call this function if you've received #GVariant
+ * data from untrusted sources and you want to ensure your serialised
+ * output is definitely in normal form.
+ *
+ * Since: 2.24
+ **/
+GVariant *
+g_variant_get_normal_form (GVariant *value)
+{
+ GVariant *trusted;
+
+ if (g_variant_is_normal_form (value))
+ return g_variant_ref (value);
+
+ trusted = g_variant_deep_copy (value);
+ g_assert (g_variant_is_trusted (trusted));
+
+ return g_variant_ref_sink (trusted);
+}
+
+/**
+ * g_variant_byteswap:
+ * @value: a #GVariant
+ * @returns: the byteswapped form of @value
+ *
+ * Performs a byteswapping operation on the contents of @value. The
+ * result is that all multi-byte numeric data contained in @value is
+ * byteswapped. That includes 16, 32, and 64bit signed and unsigned
+ * integers as well as file handles and double precision floating point
+ * values.
+ *
+ * This function is an identity mapping on any value that does not
+ * contain multi-byte numeric data. That include strings, booleans,
+ * bytes and containers containing only these things (recursively).
+ *
+ * The returned value is always in normal form and is marked as trusted.
+ *
+ * Since: 2.24
+ **/
+GVariant *
+g_variant_byteswap (GVariant *value)
+{
+ GVariantSerialised serialised;
+ GVariant *trusted;
+ GBuffer *buffer;
+ GVariant *new;
+
+ trusted = g_variant_get_normal_form (value);
+ serialised.type_info = g_variant_get_type_info (trusted);
+ serialised.size = g_variant_get_size (trusted);
+ serialised.data = g_malloc (serialised.size);
+ g_variant_store (trusted, serialised.data);
+ g_variant_unref (trusted);
+
+ g_variant_serialised_byteswap (serialised);
+
+ buffer = g_buffer_new_take_data (serialised.data, serialised.size);
+ new = g_variant_new_from_buffer (g_variant_get_type (value), buffer, TRUE);
+ g_buffer_unref (buffer);
+
+ return g_variant_ref_sink (new);
+}
+
+/**
+ * g_variant_new_from_data:
+ * @type: a #GVariantType
+ * @data: the serialised data
+ * @size: the size of @data
+ * @trusted: %TRUE if @data is definitely in normal form
+ * @notify: function to call when @data is no longer needed
+ * @user_data: data for @notify
+ * @returns: a new floating #GVariant of type @type
+ *
+ * Creates a new #GVariant instance from serialised data.
+ *
+ * @type is the type of #GVariant instance that will be constructed.
+ * The interpretation of @data depends on knowing the type.
+ *
+ * @data is not modified by this function and must remain valid with an
+ * unchanging value until such a time as @notify is called with
+ * @user_data. If the contents of @data change before that time then
+ * the result is undefined.
+ *
+ * If @data is trusted to be serialised data in normal form then
+ * @trusted should be %TRUE. This applies to serialised data created
+ * within this process or read from a trusted location on the disk (such
+ * as a file installed in /usr/lib alongside your application). You
+ * should set trusted to %FALSE if @data is read from the network, a
+ * file in the user's home directory, etc.
+ *
+ * @notify will be called with @user_data when @data is no longer
+ * needed. The exact time of this call is unspecified and might even be
+ * before this function returns.
+ *
+ * Since: 2.24
+ **/
+GVariant *
+g_variant_new_from_data (const GVariantType *type,
+ gconstpointer data,
+ gsize size,
+ gboolean trusted,
+ GDestroyNotify notify,
+ gpointer user_data)
+{
+ GVariant *value;
+ GBuffer *buffer;
+
+ if (notify)
+ buffer = g_buffer_new_from_pointer (data, size, notify, user_data);
+ else
+ buffer = g_buffer_new_from_static_data (data, size);
+
+ value = g_variant_new_from_buffer (type, buffer, trusted);
+ g_buffer_unref (buffer);
+
+ return value;
+}
+
/* Epilogue {{{1 */
#define __G_VARIANT_C__
#include "galiasdef.c"
g_variant_type_info_assert_no_infos ();
}
+static void
+test_gv_byteswap ()
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+# define native16(x) x, 0
+# define swapped16(x) 0, x
+#else
+# define native16(x) 0, x
+# define swapped16(x) x, 0
+#endif
+ /* all kinds of of crazy randomised testing already performed on the
+ * byteswapper in the /gvariant/serialiser/byteswap test and all kinds
+ * of crazy randomised testing performed against the serialiser
+ * normalisation functions in the /gvariant/serialiser/fuzz/ tests.
+ *
+ * just test a few simple cases here to make sure they each work
+ */
+ guchar valid_data[] = { 'a', '\0', swapped16(66), 2,
+ 0,
+ 'b', '\0', swapped16(77), 2,
+ 5, 11 };
+ guchar corrupt_data[] = { 'a', '\0', swapped16(66), 2,
+ 0,
+ 'b', '\0', swapped16(77), 2,
+ 6, 11 };
+ GVariant *value, *swapped, *reswapped;
+ gchar *string, *string2;
+
+
+ /* trusted */
+ value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+ valid_data, sizeof valid_data, TRUE,
+ NULL, NULL);
+ swapped = g_variant_byteswap (value);
+ g_variant_unref (value);
+ g_assert (g_variant_get_size (swapped) == 13);
+ string = g_variant_print (swapped, FALSE);
+ g_variant_unref (swapped);
+ g_assert_cmpstr (string, ==, "[('a', 66), ('b', 77)]");
+ g_free (string);
+
+ /* untrusted but valid */
+ value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+ valid_data, sizeof valid_data, FALSE,
+ NULL, NULL);
+ swapped = g_variant_byteswap (value);
+ g_variant_unref (value);
+ g_assert (g_variant_get_size (swapped) == 13);
+ string = g_variant_print (swapped, FALSE);
+ g_variant_unref (swapped);
+ g_assert_cmpstr (string, ==, "[('a', 66), ('b', 77)]");
+ g_free (string);
+
+ /* untrusted, invalid */
+ value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+ corrupt_data, sizeof corrupt_data, FALSE,
+ NULL, NULL);
+ string = g_variant_print (value, FALSE);
+ swapped = g_variant_byteswap (value);
+ g_variant_unref (value);
+ g_assert (g_variant_get_size (swapped) == 13);
+ value = g_variant_byteswap (swapped);
+ g_variant_unref (swapped);
+ string2 = g_variant_print (value, FALSE);
+ g_assert (g_variant_get_size (value) == 13);
+ g_variant_unref (value);
+ g_assert_cmpstr (string, ==, string2);
+ g_free (string2);
+ g_free (string);
+}
+
int
main (int argc, char **argv)
{
g_test_add_func ("/gvariant/valist", test_valist);
g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
g_test_add_func ("/gvariant/hashing", test_hashing);
+ g_test_add_func ("/gvariant/byteswap", test_gv_byteswap);
return g_test_run ();
}