glib/gio/tests/gdbus-serialization.c

1941 lines
70 KiB
C
Raw Normal View History

/* GLib testing framework examples and tests
*
2010-05-10 14:07:28 +02:00
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
2014-01-23 12:58:29 +01:00
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <locale.h>
#include <gio/gio.h>
#include <string.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <dbus/dbus.h>
/* ---------------------------------------------------------------------------------------------------- */
static void
hexdump (const guchar *str, gsize len)
{
const guchar *data = (const guchar *) str;
guint n, m;
for (n = 0; n < len; n += 16)
{
g_printerr ("%04x: ", n);
for (m = n; m < n + 16; m++)
{
if (m > n && (m%4) == 0)
g_printerr (" ");
if (m < len)
g_printerr ("%02x ", data[m]);
else
g_printerr (" ");
}
g_printerr (" ");
for (m = n; m < len && m < n + 16; m++)
g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.');
g_printerr ("\n");
}
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
append_gv_to_dbus_iter (DBusMessageIter *iter,
GVariant *value,
GError **error)
{
const GVariantType *type;
type = g_variant_get_type (value);
if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
{
dbus_bool_t v = g_variant_get_boolean (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
{
guint8 v = g_variant_get_byte (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
{
gint16 v = g_variant_get_int16 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
{
guint16 v = g_variant_get_uint16 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
{
gint32 v = g_variant_get_int32 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
{
guint32 v = g_variant_get_uint32 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
{
gint64 v = g_variant_get_int64 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
{
guint64 v = g_variant_get_uint64 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
{
gdouble v = g_variant_get_double (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v);
}
else if (g_variant_type_is_variant (type))
{
DBusMessageIter sub;
GVariant *child;
child = g_variant_get_child_value (value, 0);
dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT,
g_variant_get_type_string (child),
&sub);
if (!append_gv_to_dbus_iter (&sub, child, error))
{
g_variant_unref (child);
goto fail;
}
dbus_message_iter_close_container (iter, &sub);
g_variant_unref (child);
}
else if (g_variant_type_is_array (type))
{
DBusMessageIter dbus_iter;
const gchar *type_string;
GVariantIter gv_iter;
GVariant *item;
type_string = g_variant_get_type_string (value);
type_string++; /* skip the 'a' */
dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
type_string, &dbus_iter);
g_variant_iter_init (&gv_iter, value);
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
{
goto fail;
}
}
dbus_message_iter_close_container (iter, &dbus_iter);
}
else if (g_variant_type_is_tuple (type))
{
DBusMessageIter dbus_iter;
GVariantIter gv_iter;
GVariant *item;
dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT,
NULL, &dbus_iter);
g_variant_iter_init (&gv_iter, value);
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
goto fail;
}
dbus_message_iter_close_container (iter, &dbus_iter);
}
else if (g_variant_type_is_dict_entry (type))
{
DBusMessageIter dbus_iter;
GVariant *key, *val;
dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY,
NULL, &dbus_iter);
key = g_variant_get_child_value (value, 0);
if (!append_gv_to_dbus_iter (&dbus_iter, key, error))
{
g_variant_unref (key);
goto fail;
}
g_variant_unref (key);
val = g_variant_get_child_value (value, 1);
if (!append_gv_to_dbus_iter (&dbus_iter, val, error))
{
g_variant_unref (val);
goto fail;
}
g_variant_unref (val);
dbus_message_iter_close_container (iter, &dbus_iter);
}
else
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Error serializing GVariant with type-string '%s' to a D-Bus message",
g_variant_get_type_string (value));
goto fail;
}
return TRUE;
fail:
return FALSE;
}
static gboolean
append_gv_to_dbus_message (DBusMessage *message,
GVariant *value,
GError **error)
{
gboolean ret;
guint n;
ret = FALSE;
if (value != NULL)
{
DBusMessageIter iter;
GVariantIter gv_iter;
GVariant *item;
dbus_message_iter_init_append (message, &iter);
g_variant_iter_init (&gv_iter, value);
n = 0;
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&iter, item, error))
{
g_prefix_error (error,
"Error encoding in-arg %d: ",
n);
goto out;
}
n++;
}
}
ret = TRUE;
out:
return ret;
}
static void
print_gv_dbus_message (GVariant *value)
{
DBusMessage *message;
char *blob;
int blob_len;
GError *error;
message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
dbus_message_set_serial (message, 0x41);
dbus_message_set_path (message, "/foo/bar");
dbus_message_set_member (message, "Member");
error = NULL;
if (!append_gv_to_dbus_message (message, value, &error))
{
g_printerr ("Error printing GVariant as DBusMessage: %s", error->message);
g_error_free (error);
goto out;
}
dbus_message_marshal (message, &blob, &blob_len);
g_printerr ("\n");
hexdump ((guchar *) blob, blob_len);
out:
dbus_message_unref (message);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
dbus_1_message_append (GString *s,
guint indent,
DBusMessageIter *iter)
{
gint arg_type;
DBusMessageIter sub;
g_string_append_printf (s, "%*s", indent, "");
arg_type = dbus_message_iter_get_arg_type (iter);
switch (arg_type)
{
case DBUS_TYPE_BOOLEAN:
{
dbus_bool_t value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "bool: %s\n", value ? "true" : "false");
break;
}
case DBUS_TYPE_BYTE:
{
guchar value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "byte: 0x%02x\n", (guint) value);
break;
}
case DBUS_TYPE_INT16:
{
gint16 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT16:
{
guint16 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value);
break;
}
case DBUS_TYPE_INT32:
{
gint32 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT32:
{
guint32 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value);
break;
}
case DBUS_TYPE_INT64:
{
gint64 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT64:
{
guint64 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value);
break;
}
case DBUS_TYPE_DOUBLE:
{
gdouble value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "double: %f\n", value);
break;
}
case DBUS_TYPE_STRING:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "string: '%s'\n", value);
break;
}
case DBUS_TYPE_OBJECT_PATH:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "object_path: '%s'\n", value);
break;
}
case DBUS_TYPE_SIGNATURE:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "signature: '%s'\n", value);
break;
}
#ifdef DBUS_TYPE_UNIX_FD
case DBUS_TYPE_UNIX_FD:
{
/* unfortunately there's currently no way to get just the
* protocol value, since dbus_message_iter_get_basic() wants
* to be 'helpful' and dup the fd for the user...
*/
g_string_append (s, "unix-fd: (not extracted)\n");
break;
}
#endif
case DBUS_TYPE_VARIANT:
g_string_append_printf (s, "variant:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_ARRAY:
g_string_append_printf (s, "array:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_STRUCT:
g_string_append_printf (s, "struct:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_DICT_ENTRY:
g_string_append_printf (s, "dict_entry:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
default:
g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type '%c' (%d)",
arg_type,
arg_type);
g_assert_not_reached ();
break;
}
}
static gchar *
dbus_1_message_print (DBusMessage *message)
{
GString *s;
guint n;
DBusMessageIter iter;
s = g_string_new (NULL);
n = 0;
dbus_message_iter_init (message, &iter);
while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
{
g_string_append_printf (s, "value %d: ", n);
dbus_1_message_append (s, 2, &iter);
dbus_message_iter_next (&iter);
n++;
}
return g_string_free (s, FALSE);
}
/* ---------------------------------------------------------------------------------------------------- */
static gchar *
get_body_signature (GVariant *value)
{
const gchar *s;
gsize len;
gchar *ret;
if (value == NULL)
{
ret = g_strdup ("");
goto out;
}
s = g_variant_get_type_string (value);
len = strlen (s);
g_assert (len >= 2);
ret = g_strndup (s + 1, len - 2);
out:
return ret;
}
/* If @value is floating, this assumes ownership. */
static gchar *
get_and_check_serialization (GVariant *value)
{
guchar *blob;
gsize blob_size;
DBusMessage *dbus_1_message;
GDBusMessage *message;
GDBusMessage *recovered_message;
GError *error;
DBusError dbus_error;
gchar *last_serialization = NULL;
gchar *s = NULL;
guint n;
message = g_dbus_message_new ();
g_dbus_message_set_body (message, value);
2010-05-06 23:31:51 +02:00
g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
g_dbus_message_set_serial (message, 0x41);
s = get_body_signature (value);
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s));
g_free (s);
/* First check that the serialization to the D-Bus wire format is correct - do this for both byte orders */
for (n = 0; n < 2; n++)
{
GDBusMessageByteOrder byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
switch (n)
{
case 0:
byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
break;
case 1:
byte_order = G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN;
break;
case 2:
g_assert_not_reached ();
break;
}
g_dbus_message_set_byte_order (message, byte_order);
error = NULL;
blob = g_dbus_message_to_blob (message,
&blob_size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&error);
g_assert_no_error (error);
g_assert (blob != NULL);
switch (byte_order)
{
case G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN:
g_assert_cmpint (blob[0], ==, 'B');
break;
case G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN:
g_assert_cmpint (blob[0], ==, 'l');
break;
}
dbus_error_init (&dbus_error);
dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error);
if (dbus_error_is_set (&dbus_error))
{
g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n",
dbus_error.name,
dbus_error.message);
hexdump (blob, blob_size);
dbus_error_free (&dbus_error);
s = g_variant_print (value, TRUE);
g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s);
g_free (s);
g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n");
print_gv_dbus_message (value);
g_assert_not_reached ();
}
s = dbus_1_message_print (dbus_1_message);
dbus_message_unref (dbus_1_message);
/* Then serialize back and check that the body is identical */
error = NULL;
recovered_message = g_dbus_message_new_from_blob (blob,
blob_size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&error);
g_assert_no_error (error);
g_assert (recovered_message != NULL);
if (value == NULL)
{
g_assert (g_dbus_message_get_body (recovered_message) == NULL);
}
else
{
g_assert (g_dbus_message_get_body (recovered_message) != NULL);
g_assert_cmpvariant (g_dbus_message_get_body (recovered_message), value);
}
g_object_unref (recovered_message);
g_free (blob);
if (last_serialization != NULL)
{
g_assert_cmpstr (last_serialization, ==, s);
g_free (last_serialization);
}
last_serialization = g_steal_pointer (&s);
}
g_object_unref (message);
return g_steal_pointer (&last_serialization);
}
/* If @value is floating, this assumes ownership. */
static void
check_serialization (GVariant *value,
const gchar *expected_dbus_1_output)
{
gchar *s = get_and_check_serialization (value);
g_assert_cmpstr (s, ==, expected_dbus_1_output);
g_free (s);
}
static void
test_message_serialize_basic (void)
{
check_serialization (NULL, "");
check_serialization (g_variant_new ("(sogybnqiuxtd)",
"this is a string",
"/this/is/a/path",
"sad",
42,
TRUE,
-42,
60000,
-44,
100000,
-(G_GUINT64_CONSTANT(2)<<34),
G_GUINT64_CONSTANT(0xffffffffffffffff),
42.5),
"value 0: string: 'this is a string'\n"
"value 1: object_path: '/this/is/a/path'\n"
"value 2: signature: 'sad'\n"
"value 3: byte: 0x2a\n"
"value 4: bool: true\n"
"value 5: int16: -42\n"
"value 6: uint16: 60000\n"
"value 7: int32: -44\n"
"value 8: uint32: 100000\n"
"value 9: int64: -34359738368\n"
"value 10: uint64: 18446744073709551615\n"
"value 11: double: 42.500000\n");
}
/* ---------------------------------------------------------------------------------------------------- */
static void
test_message_serialize_complex (void)
{
GError *error;
GVariant *value;
guint i;
gchar *serialization = NULL;
error = NULL;
GDBus: Fix serialization of empty arrays It turns out that we didn't observe padding (neither when reading nor writing) for empty arrays which (apparently) is needed according to the D-Bus spec and reference implementation. A simple test case to provoke this behavior is as follows (notice the lack of 4 bytes worth of padding at position 0x0064): Error calling dbus_message_demarshal() on this blob: org.freedesktop.DBus.Error.InvalidArgs: Message is corrupted (Alignment padding not null) 0000: 6c 01 00 01 2e 00 00 00 41 00 00 00 37 00 00 00 l.......A...7... 0010: 08 01 67 00 08 73 61 7b 73 76 7d 61 73 00 00 00 ..g..sa{sv}as... 0020: 01 01 6f 00 08 00 00 00 2f 66 6f 6f 2f 62 61 72 ..o...../foo/bar 0030: 00 00 00 00 00 00 00 00 03 01 73 00 06 00 00 00 ..........s..... 0040: 4d 65 6d 62 65 72 00 00 11 00 00 00 30 31 32 33 Member......0123 0050: 34 35 36 37 38 39 30 31 32 33 34 35 36 00 00 00 4567890123456... 0060: 00 00 00 00 0e 00 00 00 09 00 00 00 53 6f 6d 65 ............Some 0070: 74 68 69 6e 67 00 thing. The blob was generated from the following GVariant value: ('01234567890123456', @a{sv} {}, ['Something']) If the blob was encoded using DBusMessageIter, the payload would have been: 0000: 6c 01 00 01 32 00 00 00 41 00 00 00 36 00 00 00 l...2...A...6... 0010: 01 01 6f 00 08 00 00 00 2f 66 6f 6f 2f 62 61 72 ..o...../foo/bar 0020: 00 00 00 00 00 00 00 00 03 01 73 00 06 00 00 00 ..........s..... 0030: 4d 65 6d 62 65 72 00 00 08 01 67 00 08 73 61 7b Member....g..sa{ 0040: 73 76 7d 61 73 00 00 00 11 00 00 00 30 31 32 33 sv}as.......0123 0050: 34 35 36 37 38 39 30 31 32 33 34 35 36 00 00 00 4567890123456... 0060: 00 00 00 00 00 00 00 00 0e 00 00 00 09 00 00 00 ................ 0070: 53 6f 6d 65 74 68 69 6e 67 00 Something. ** ERROR:gdbus-serialization.c:547:check_serialization: code should not be reached Aborted and this is now in the libdbus-1-using serialization test case. Signed-off-by: David Zeuthen <davidz@redhat.com>
2010-05-14 18:49:51 +02:00
value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"),
"([1, 2, 3], {'one': 'white', 'two': 'black'})",
NULL, NULL, &error);
g_assert_no_error (error);
g_assert (value != NULL);
check_serialization (value,
"value 0: array:\n"
" int32: 1\n"
" int32: 2\n"
" int32: 3\n"
"value 1: array:\n"
" dict_entry:\n"
" string: 'one'\n"
" string: 'white'\n"
" dict_entry:\n"
" string: 'two'\n"
" string: 'black'\n");
g_variant_unref (value);
GDBus: Fix serialization of empty arrays It turns out that we didn't observe padding (neither when reading nor writing) for empty arrays which (apparently) is needed according to the D-Bus spec and reference implementation. A simple test case to provoke this behavior is as follows (notice the lack of 4 bytes worth of padding at position 0x0064): Error calling dbus_message_demarshal() on this blob: org.freedesktop.DBus.Error.InvalidArgs: Message is corrupted (Alignment padding not null) 0000: 6c 01 00 01 2e 00 00 00 41 00 00 00 37 00 00 00 l.......A...7... 0010: 08 01 67 00 08 73 61 7b 73 76 7d 61 73 00 00 00 ..g..sa{sv}as... 0020: 01 01 6f 00 08 00 00 00 2f 66 6f 6f 2f 62 61 72 ..o...../foo/bar 0030: 00 00 00 00 00 00 00 00 03 01 73 00 06 00 00 00 ..........s..... 0040: 4d 65 6d 62 65 72 00 00 11 00 00 00 30 31 32 33 Member......0123 0050: 34 35 36 37 38 39 30 31 32 33 34 35 36 00 00 00 4567890123456... 0060: 00 00 00 00 0e 00 00 00 09 00 00 00 53 6f 6d 65 ............Some 0070: 74 68 69 6e 67 00 thing. The blob was generated from the following GVariant value: ('01234567890123456', @a{sv} {}, ['Something']) If the blob was encoded using DBusMessageIter, the payload would have been: 0000: 6c 01 00 01 32 00 00 00 41 00 00 00 36 00 00 00 l...2...A...6... 0010: 01 01 6f 00 08 00 00 00 2f 66 6f 6f 2f 62 61 72 ..o...../foo/bar 0020: 00 00 00 00 00 00 00 00 03 01 73 00 06 00 00 00 ..........s..... 0030: 4d 65 6d 62 65 72 00 00 08 01 67 00 08 73 61 7b Member....g..sa{ 0040: 73 76 7d 61 73 00 00 00 11 00 00 00 30 31 32 33 sv}as.......0123 0050: 34 35 36 37 38 39 30 31 32 33 34 35 36 00 00 00 4567890123456... 0060: 00 00 00 00 00 00 00 00 0e 00 00 00 09 00 00 00 ................ 0070: 53 6f 6d 65 74 68 69 6e 67 00 Something. ** ERROR:gdbus-serialization.c:547:check_serialization: code should not be reached Aborted and this is now in the libdbus-1-using serialization test case. Signed-off-by: David Zeuthen <davidz@redhat.com>
2010-05-14 18:49:51 +02:00
value = g_variant_parse (G_VARIANT_TYPE ("(sa{sv}as)"),
"('01234567890123456', {}, ['Something'])",
NULL, NULL, &error);
g_assert_no_error (error);
g_assert (value != NULL);
check_serialization (value,
"value 0: string: '01234567890123456'\n"
GDBus: Fix serialization of empty arrays It turns out that we didn't observe padding (neither when reading nor writing) for empty arrays which (apparently) is needed according to the D-Bus spec and reference implementation. A simple test case to provoke this behavior is as follows (notice the lack of 4 bytes worth of padding at position 0x0064): Error calling dbus_message_demarshal() on this blob: org.freedesktop.DBus.Error.InvalidArgs: Message is corrupted (Alignment padding not null) 0000: 6c 01 00 01 2e 00 00 00 41 00 00 00 37 00 00 00 l.......A...7... 0010: 08 01 67 00 08 73 61 7b 73 76 7d 61 73 00 00 00 ..g..sa{sv}as... 0020: 01 01 6f 00 08 00 00 00 2f 66 6f 6f 2f 62 61 72 ..o...../foo/bar 0030: 00 00 00 00 00 00 00 00 03 01 73 00 06 00 00 00 ..........s..... 0040: 4d 65 6d 62 65 72 00 00 11 00 00 00 30 31 32 33 Member......0123 0050: 34 35 36 37 38 39 30 31 32 33 34 35 36 00 00 00 4567890123456... 0060: 00 00 00 00 0e 00 00 00 09 00 00 00 53 6f 6d 65 ............Some 0070: 74 68 69 6e 67 00 thing. The blob was generated from the following GVariant value: ('01234567890123456', @a{sv} {}, ['Something']) If the blob was encoded using DBusMessageIter, the payload would have been: 0000: 6c 01 00 01 32 00 00 00 41 00 00 00 36 00 00 00 l...2...A...6... 0010: 01 01 6f 00 08 00 00 00 2f 66 6f 6f 2f 62 61 72 ..o...../foo/bar 0020: 00 00 00 00 00 00 00 00 03 01 73 00 06 00 00 00 ..........s..... 0030: 4d 65 6d 62 65 72 00 00 08 01 67 00 08 73 61 7b Member....g..sa{ 0040: 73 76 7d 61 73 00 00 00 11 00 00 00 30 31 32 33 sv}as.......0123 0050: 34 35 36 37 38 39 30 31 32 33 34 35 36 00 00 00 4567890123456... 0060: 00 00 00 00 00 00 00 00 0e 00 00 00 09 00 00 00 ................ 0070: 53 6f 6d 65 74 68 69 6e 67 00 Something. ** ERROR:gdbus-serialization.c:547:check_serialization: code should not be reached Aborted and this is now in the libdbus-1-using serialization test case. Signed-off-by: David Zeuthen <davidz@redhat.com>
2010-05-14 18:49:51 +02:00
"value 1: array:\n"
"value 2: array:\n"
" string: 'Something'\n");
g_variant_unref (value);
GDBusMessage: Fix bug when deserializing a message See https://bugzilla.gnome.org/show_bug.cgi?id=621838 for the whole story. The problem was that we ended up reading data from arrays of arrays when we were just supposed to be aligning the buffers. Also add a host of debug infrastructure that was needed to find the root cause. For now it can be turned on only via defining DEBUG_SERIALIZER. In the future we might want to make it work via G_DBUS_DEBUG. In a nutshell, the added debug info looks like this Parsing blob (blob_len = 0x0084 bytes) 0000: 6c 01 00 01 3c 00 00 00 41 00 00 00 37 00 00 00 l...<...A...7... 0010: 08 01 67 00 08 61 61 79 61 7b 73 76 7d 00 00 00 ..g..aaya{sv}... 0020: 01 01 6f 00 08 00 00 00 2f 66 6f 6f 2f 62 61 72 ..o...../foo/bar 0030: 00 00 00 00 00 00 00 00 03 01 73 00 06 00 00 00 ..........s..... 0040: 4d 65 6d 62 65 72 00 00 00 00 00 00 34 00 00 00 Member......4... 0050: 03 00 00 00 63 77 64 00 01 73 00 00 23 00 00 00 ....cwd..s..#... 0060: 2f 68 6f 6d 65 2f 64 61 76 69 64 7a 2f 48 61 63 /home/davidz/Hac 0070: 6b 69 6e 67 2f 67 6c 69 62 2f 67 69 6f 2f 74 65 king/glib/gio/te 0080: 73 74 73 00 sts. Parsing headers (blob_len = 0x0084 bytes) Reading type a{yv} from offset 0x000c: array spans 0x0037 bytes Reading type {yv} from offset 0x0010 Reading type y from offset 0x0010: 0x08 ' Reading type v from offset 0x0011 Reading type g from offset 0x0014: 'aaya{sv}' Reading type {yv} from offset 0x001e Reading type y from offset 0x0020: 0x01 '' Reading type v from offset 0x0021 Reading type o from offset 0x0024: '/foo/bar' Reading type {yv} from offset 0x0031 Reading type y from offset 0x0038: 0x03 '' Reading type v from offset 0x0039 Reading type s from offset 0x003c: 'Member' Parsing body (blob_len = 0x0084 bytes) Reading type (aaya{sv}) from offset 0x0047 Reading type aay from offset 0x0048: array spans 0x0000 bytes Reading type a{sv} from offset 0x004c: array spans 0x0034 bytes Reading type {sv} from offset 0x0050 Reading type s from offset 0x0050: 'cwd' Reading type v from offset 0x0058 Reading type s from offset 0x005b: '/home/davidz/Hacking/glib/gio/tests' OK Signed-off-by: David Zeuthen <davidz@redhat.com>
2010-06-17 23:58:25 +02:00
/* https://bugzilla.gnome.org/show_bug.cgi?id=621838 */
check_serialization (g_variant_new_parsed ("(@aay [], {'cwd': <'/home/davidz/Hacking/glib/gio/tests'>})"),
"value 0: array:\n"
"value 1: array:\n"
" dict_entry:\n"
" string: 'cwd'\n"
GDBusMessage: Fix bug when deserializing a message See https://bugzilla.gnome.org/show_bug.cgi?id=621838 for the whole story. The problem was that we ended up reading data from arrays of arrays when we were just supposed to be aligning the buffers. Also add a host of debug infrastructure that was needed to find the root cause. For now it can be turned on only via defining DEBUG_SERIALIZER. In the future we might want to make it work via G_DBUS_DEBUG. In a nutshell, the added debug info looks like this Parsing blob (blob_len = 0x0084 bytes) 0000: 6c 01 00 01 3c 00 00 00 41 00 00 00 37 00 00 00 l...<...A...7... 0010: 08 01 67 00 08 61 61 79 61 7b 73 76 7d 00 00 00 ..g..aaya{sv}... 0020: 01 01 6f 00 08 00 00 00 2f 66 6f 6f 2f 62 61 72 ..o...../foo/bar 0030: 00 00 00 00 00 00 00 00 03 01 73 00 06 00 00 00 ..........s..... 0040: 4d 65 6d 62 65 72 00 00 00 00 00 00 34 00 00 00 Member......4... 0050: 03 00 00 00 63 77 64 00 01 73 00 00 23 00 00 00 ....cwd..s..#... 0060: 2f 68 6f 6d 65 2f 64 61 76 69 64 7a 2f 48 61 63 /home/davidz/Hac 0070: 6b 69 6e 67 2f 67 6c 69 62 2f 67 69 6f 2f 74 65 king/glib/gio/te 0080: 73 74 73 00 sts. Parsing headers (blob_len = 0x0084 bytes) Reading type a{yv} from offset 0x000c: array spans 0x0037 bytes Reading type {yv} from offset 0x0010 Reading type y from offset 0x0010: 0x08 ' Reading type v from offset 0x0011 Reading type g from offset 0x0014: 'aaya{sv}' Reading type {yv} from offset 0x001e Reading type y from offset 0x0020: 0x01 '' Reading type v from offset 0x0021 Reading type o from offset 0x0024: '/foo/bar' Reading type {yv} from offset 0x0031 Reading type y from offset 0x0038: 0x03 '' Reading type v from offset 0x0039 Reading type s from offset 0x003c: 'Member' Parsing body (blob_len = 0x0084 bytes) Reading type (aaya{sv}) from offset 0x0047 Reading type aay from offset 0x0048: array spans 0x0000 bytes Reading type a{sv} from offset 0x004c: array spans 0x0034 bytes Reading type {sv} from offset 0x0050 Reading type s from offset 0x0050: 'cwd' Reading type v from offset 0x0058 Reading type s from offset 0x005b: '/home/davidz/Hacking/glib/gio/tests' OK Signed-off-by: David Zeuthen <davidz@redhat.com>
2010-06-17 23:58:25 +02:00
" variant:\n"
" string: '/home/davidz/Hacking/glib/gio/tests'\n");
#ifdef DBUS_TYPE_UNIX_FD
value = g_variant_parse (G_VARIANT_TYPE ("(hah)"),
"(42, [43, 44])",
NULL, NULL, &error);
g_assert_no_error (error);
g_assert (value != NULL);
/* about (not extracted), see comment in DBUS_TYPE_UNIX_FD case in
* dbus_1_message_append() above.
*/
check_serialization (value,
"value 0: unix-fd: (not extracted)\n"
"value 1: array:\n"
" unix-fd: (not extracted)\n"
" unix-fd: (not extracted)\n");
g_variant_unref (value);
#endif
/* Deep nesting of variants (just below the recursion limit). */
value = g_variant_new_string ("buried");
for (i = 0; i < 64; i++)
value = g_variant_new_variant (value);
value = g_variant_new_tuple (&value, 1);
serialization = get_and_check_serialization (value);
g_assert_nonnull (serialization);
g_assert_true (g_str_has_prefix (serialization,
"value 0: variant:\n"
" variant:\n"
" variant:\n"));
g_free (serialization);
/* Deep nesting of arrays and structs (just below the recursion limit).
* See https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-signature */
value = g_variant_new_string ("hello");
for (i = 0; i < 32; i++)
value = g_variant_new_tuple (&value, 1);
for (i = 0; i < 32; i++)
value = g_variant_new_array (NULL, &value, 1);
value = g_variant_new_tuple (&value, 1);
serialization = get_and_check_serialization (value);
g_assert_nonnull (serialization);
g_assert_true (g_str_has_prefix (serialization,
"value 0: array:\n"
" array:\n"
" array:\n"));
g_free (serialization);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
replace (char *blob,
gsize len,
const char *before,
const char *after)
{
gsize i;
gsize slen = strlen (before) + 1;
g_assert_cmpuint (strlen (before), ==, strlen (after));
g_assert_cmpuint (len, >=, slen);
for (i = 0; i < (len - slen + 1); i++)
{
if (memcmp (blob + i, before, slen) == 0)
memcpy (blob + i, after, slen);
}
}
static void
test_message_serialize_invalid (void)
{
guint n;
/* Other things we could check (note that GDBus _does_ check for all
* these things - we just don't have test-suit coverage for it)
*
* - array exceeding 64 MiB (2^26 bytes) - unfortunately libdbus-1 checks
* this, e.g.
*
* process 19620: arguments to dbus_message_iter_append_fixed_array() were incorrect,
* assertion "n_elements <= DBUS_MAXIMUM_ARRAY_LENGTH / _dbus_type_get_alignment (element_type)"
* failed in file dbus-message.c line 2344.
* This is normally a bug in some application using the D-Bus library.
* D-Bus not built with -rdynamic so unable to print a backtrace
* Aborted (core dumped)
*
* - message exceeding 128 MiB (2^27 bytes)
*
* - endianness, message type, flags, protocol version
*/
for (n = 0; n < 3; n++)
{
GDBusMessage *message;
GError *error;
DBusMessage *dbus_message;
char *blob;
int blob_len;
/* these are in pairs with matching length */
const gchar *valid_utf8_str = "this is valid...";
const gchar *invalid_utf8_str = "this is invalid\xff";
const gchar *valid_signature = "a{sv}a{sv}a{sv}aiai";
const gchar *invalid_signature = "not valid signature";
const gchar *valid_object_path = "/this/is/a/valid/dbus/object/path";
const gchar *invalid_object_path = "/this/is/not a valid object path!";
dbus_message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
dbus_message_set_serial (dbus_message, 0x41);
dbus_message_set_path (dbus_message, "/foo/bar");
dbus_message_set_member (dbus_message, "Member");
switch (n)
{
case 0:
/* invalid UTF-8 */
dbus_message_append_args (dbus_message,
DBUS_TYPE_STRING, &valid_utf8_str,
DBUS_TYPE_INVALID);
break;
case 1:
/* invalid object path */
dbus_message_append_args (dbus_message,
DBUS_TYPE_OBJECT_PATH, &valid_object_path,
DBUS_TYPE_INVALID);
break;
case 2:
/* invalid signature */
dbus_message_append_args (dbus_message,
DBUS_TYPE_SIGNATURE, &valid_signature,
DBUS_TYPE_INVALID);
break;
default:
g_assert_not_reached ();
break;
}
dbus_message_marshal (dbus_message, &blob, &blob_len);
/* hack up the message to be invalid by replacing each valid string
* with its invalid counterpart */
replace (blob, blob_len, valid_utf8_str, invalid_utf8_str);
replace (blob, blob_len, valid_object_path, invalid_object_path);
replace (blob, blob_len, valid_signature, invalid_signature);
error = NULL;
message = g_dbus_message_new_from_blob ((guchar *) blob,
blob_len,
G_DBUS_CAPABILITY_FLAGS_NONE,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_error_free (error);
g_assert (message == NULL);
dbus_free (blob);
dbus_message_unref (dbus_message);
}
}
/* ---------------------------------------------------------------------------------------------------- */
static void
test_message_serialize_header_checks (void)
{
GDBusMessage *message;
GDBusMessage *reply;
GError *error = NULL;
guchar *blob;
gsize blob_size;
/*
* check we can't serialize messages with INVALID type
*/
message = g_dbus_message_new ();
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: type is INVALID");
g_clear_error (&error);
g_assert_null (blob);
g_object_unref (message);
/*
* check we can't serialize messages with an INVALID header
*/
message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember");
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INVALID, g_variant_new_boolean (FALSE));
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: INVALID header field supplied");
g_assert_null (blob);
g_clear_error (&error);
g_clear_object (&message);
/*
* check that we can't serialize messages with various fields set to incorrectly typed values
*/
const struct
{
GDBusMessageHeaderField field;
const char *invalid_value; /* as a GVariant in text form */
const char *expected_error_message;
}
field_type_tests[] =
{
{
G_DBUS_MESSAGE_HEADER_FIELD_PATH,
"'/correct/value/but/wrong/type'",
"Cannot serialize message: SIGNAL message: PATH header field is invalid; expected a value of type o"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE,
"@u 5",
"Cannot serialize message: SIGNAL message: INTERFACE header field is invalid; expected a value of type s"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE,
"'valid type, but not an interface name'",
"Cannot serialize message: SIGNAL message: INTERFACE header field does not contain a valid interface name"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_MEMBER,
"@u 5",
"Cannot serialize message: SIGNAL message: MEMBER header field is invalid; expected a value of type s"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_MEMBER,
"'valid type, but not a member name'",
"Cannot serialize message: SIGNAL message: MEMBER header field does not contain a valid member name"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME,
"@u 5",
"Cannot serialize message: SIGNAL message: ERROR_NAME header field is invalid; expected a value of type s"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME,
"'valid type, but not an error name'",
"Cannot serialize message: SIGNAL message: ERROR_NAME header field does not contain a valid error name"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL,
"'oops'",
"Cannot serialize message: SIGNAL message: REPLY_SERIAL header field is invalid; expected a value of type u"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION,
"@u 5",
"Cannot serialize message: SIGNAL message: DESTINATION header field is invalid; expected a value of type s"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_SENDER,
"@u 5",
"Cannot serialize message: SIGNAL message: SENDER header field is invalid; expected a value of type s"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE,
"false",
"Cannot serialize message: SIGNAL message: SIGNATURE header field is invalid; expected a value of type g"
},
{
G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS,
"'five'",
"Cannot serialize message: SIGNAL message: NUM_UNIX_FDS header field is invalid; expected a value of type u"
},
};
for (size_t i = 0; i < G_N_ELEMENTS (field_type_tests); i++)
{
message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember");
g_dbus_message_set_header (message, field_type_tests[i].field, g_variant_new_parsed (field_type_tests[i].invalid_value));
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, field_type_tests[i].expected_error_message);
g_assert_null (blob);
g_clear_error (&error);
g_clear_object (&message);
}
/*
* check we can't serialize signal messages with INTERFACE, PATH or MEMBER unset / set to reserved value
*/
message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember");
/* ----- */
/* interface NULL => error */
g_dbus_message_set_interface (message, NULL);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: INTERFACE header field is missing or invalid");
g_clear_error (&error);
g_assert_null (blob);
/* interface reserved value => error */
g_dbus_message_set_interface (message, DBUS_INTERFACE_LOCAL);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: INTERFACE header field is using the reserved value " DBUS_INTERFACE_LOCAL);
g_clear_error (&error);
g_assert_null (blob);
/* reset interface */
g_dbus_message_set_interface (message, "The.Interface");
/* ----- */
/* path NULL => error */
g_dbus_message_set_path (message, NULL);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH header field is missing or invalid");
g_clear_error (&error);
g_assert_null (blob);
/* path reserved value => error */
g_dbus_message_set_path (message, DBUS_PATH_LOCAL);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH header field is using the reserved value " DBUS_PATH_LOCAL);
g_clear_error (&error);
g_assert_null (blob);
/* reset path */
g_dbus_message_set_path (message, "/the/path");
/* ----- */
/* member NULL => error */
g_dbus_message_set_member (message, NULL);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: MEMBER header field is missing or invalid");
g_clear_error (&error);
g_assert_null (blob);
/* reset member */
g_dbus_message_set_member (message, "TheMember");
/* ----- */
/* done */
g_object_unref (message);
/*
* check that we can't serialize method call messages with PATH or MEMBER unset
*/
message = g_dbus_message_new_method_call (NULL, "/the/path", NULL, "TheMember");
/* ----- */
/* path NULL => error */
g_dbus_message_set_path (message, NULL);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH header field is missing or invalid");
g_clear_error (&error);
g_assert_null (blob);
/* reset path */
g_dbus_message_set_path (message, "/the/path");
/* ----- */
/* member NULL => error */
g_dbus_message_set_member (message, NULL);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: MEMBER header field is missing or invalid");
g_clear_error (&error);
g_assert_null (blob);
/* reset member */
g_dbus_message_set_member (message, "TheMember");
/* ----- */
/* done */
g_object_unref (message);
/*
* check that we can't serialize method reply messages with REPLY_SERIAL unset
*/
message = g_dbus_message_new_method_call (NULL, "/the/path", NULL, "TheMember");
g_dbus_message_set_serial (message, 42);
/* method reply */
reply = g_dbus_message_new_method_reply (message);
g_assert_cmpint (g_dbus_message_get_reply_serial (reply), ==, 42);
g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL);
blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_RETURN message: REPLY_SERIAL header field is missing or invalid");
g_clear_error (&error);
g_assert_null (blob);
g_object_unref (reply);
/* method error - first nuke ERROR_NAME, then REPLY_SERIAL */
reply = g_dbus_message_new_method_error (message, "Some.Error.Name", "the message");
g_assert_cmpint (g_dbus_message_get_reply_serial (reply), ==, 42);
/* nuke ERROR_NAME */
g_dbus_message_set_error_name (reply, NULL);
blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: ERROR_NAME header field is missing or invalid");
g_clear_error (&error);
g_assert_null (blob);
/* reset ERROR_NAME */
g_dbus_message_set_error_name (reply, "Some.Error.Name");
/* nuke REPLY_SERIAL */
g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL);
blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL header field is missing or invalid");
g_clear_error (&error);
g_assert_null (blob);
g_object_unref (reply);
g_object_unref (message);
}
static void
test_message_serialize_header_checks_valid (void)
{
GDBusMessage *message = NULL, *reply = NULL;
GError *local_error = NULL;
guchar *blob;
gsize blob_size;
g_test_summary ("Test that validation allows well-formed messages of all the different types");
/* Method call */
message = g_dbus_message_new_method_call ("Some.Name", "/the/path", "org.some.Interface", "TheMethod");
g_dbus_message_set_serial (message, 666);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error);
g_assert_no_error (local_error);
g_assert_nonnull (blob);
g_free (blob);
/* Method return */
reply = g_dbus_message_new_method_reply (message);
blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error);
g_assert_no_error (local_error);
g_assert_nonnull (blob);
g_free (blob);
g_clear_object (&reply);
/* Error */
reply = g_dbus_message_new_method_error (message, "Error.Name", "Some error message");
blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error);
g_assert_no_error (local_error);
g_assert_nonnull (blob);
g_free (blob);
g_clear_object (&reply);
g_clear_object (&message);
/* Signal */
message = g_dbus_message_new_signal ("/the/path", "org.some.Interface", "SignalName");
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error);
g_assert_no_error (local_error);
g_assert_nonnull (blob);
g_free (blob);
g_clear_object (&message);
/* Also check that an unknown message type is allowed */
message = g_dbus_message_new ();
g_dbus_message_set_message_type (message, 123);
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error);
g_assert_no_error (local_error);
g_assert_nonnull (blob);
g_free (blob);
g_clear_object (&message);
/* Even one with a well-defined field on it */
message = g_dbus_message_new ();
g_dbus_message_set_message_type (message, 123);
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS, g_variant_new_uint32 (0));
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error);
g_assert_no_error (local_error);
g_assert_nonnull (blob);
g_free (blob);
g_clear_object (&message);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
test_message_parse_empty_arrays_of_arrays (void)
{
GVariant *body;
GError *error = NULL;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=673612");
/* These three-element array of empty arrays were previously read back as a
* two-element array of empty arrays, due to sometimes erroneously skipping
* four bytes to align for the eight-byte-aligned grandchild types (x and
* dict_entry).
*/
body = g_variant_parse (G_VARIANT_TYPE ("(aaax)"),
"([@aax [], [], []],)", NULL, NULL, &error);
g_assert_no_error (error);
check_serialization (body,
"value 0: array:\n"
" array:\n"
" array:\n"
" array:\n");
g_variant_unref (body);
body = g_variant_parse (G_VARIANT_TYPE ("(aaa{uu})"),
"([@aa{uu} [], [], []],)", NULL, NULL, &error);
g_assert_no_error (error);
check_serialization (body,
"value 0: array:\n"
" array:\n"
" array:\n"
" array:\n");
g_variant_unref (body);
/* Due to the same bug, g_dbus_message_new_from_blob() would fail for this
* message because it would try to read past the end of the string. Hence,
* sending this to an application would make it fall off the bus. */
body = g_variant_parse (G_VARIANT_TYPE ("(a(aa{sv}as))"),
"([ ([], []),"
" ([], []),"
" ([], [])],)", NULL, NULL, &error);
g_assert_no_error (error);
check_serialization (body,
"value 0: array:\n"
" struct:\n"
" array:\n"
" array:\n"
" struct:\n"
" array:\n"
" array:\n"
" struct:\n"
" array:\n"
" array:\n");
g_variant_unref (body);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
test_message_serialize_double_array (void)
{
GVariantBuilder builder;
GVariant *body;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=732754");
g_variant_builder_init_static (&builder, G_VARIANT_TYPE ("ad"));
g_variant_builder_add (&builder, "d", (gdouble)0.0);
g_variant_builder_add (&builder, "d", (gdouble)8.0);
g_variant_builder_add (&builder, "d", (gdouble)22.0);
g_variant_builder_add (&builder, "d", (gdouble)0.0);
body = g_variant_new ("(@ad)", g_variant_builder_end (&builder));
check_serialization (body,
"value 0: array:\n"
" double: 0.000000\n"
" double: 8.000000\n"
" double: 22.000000\n"
" double: 0.000000\n");
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that an invalid header in a D-Bus message (specifically, with a type
* which doesnt match whats expected for the given header) is gracefully
* handled with an error rather than a crash. */
static void
test_message_parse_non_signature_header (void)
{
const guint8 data[] = {
'l', /* little-endian byte order */
0x02, /* message type (method return) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0x00, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x00, 0x00, 0x00, 0xbc, /* message serial */
/* a{yv} of header fields:
* (things start to be invalid below here) */
0x10, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x08, /* array key (SIGNATURE) */
/* Variant array value: */
0x04, /* signature length */
'd', 0x00, 0x00, 'F', /* signature (invalid) */
0x00, /* nul terminator */
/* (Variant array value payload missing) */
/* alignment padding before the next header array element, as structs must
* be 8-aligned: */
0x00,
0x05, /* array key (REPLY_SERIAL, required for method return messages) */
/* Variant array value: */
0x01, /* signature length */
'u', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x00, 0x01, 0x02, 0x03,
/* (message body is zero-length) */
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message);
g_clear_error (&local_error);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that an invalid header in a D-Bus message (specifically, containing a
* variant with an empty type signature) is gracefully handled with an error
* rather than a crash. */
static void
test_message_parse_empty_signature_header (void)
{
const guint8 data[] = {
'l', /* little-endian byte order */
0x02, /* message type (method return) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0x00, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x20, 0x20, 0x20, 0x20, /* message serial */
/* a{yv} of header fields:
* (things start to be invalid below here) */
0x10, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x20, /* array key (this is not currently a valid header field) */
/* Variant array value: */
0x00, /* signature length */
0x00, /* nul terminator */
/* (Variant array value payload missing) */
/* alignment padding before the next header array element, as structs must
* be 8-aligned: */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, /* array key (REPLY_SERIAL, required for method return messages) */
/* Variant array value: */
0x01, /* signature length */
'u', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x00, 0x01, 0x02, 0x03,
/* (message body is zero-length) */
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message);
g_clear_error (&local_error);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that an invalid header in a D-Bus message (specifically, containing a
* variant with a type signature containing multiple complete types) is
* gracefully handled with an error rather than a crash. */
static void
test_message_parse_multiple_signature_header (void)
{
const guint8 data[] = {
'l', /* little-endian byte order */
0x02, /* message type (method return) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0x00, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x20, 0x20, 0x20, 0x20, /* message serial */
/* a{yv} of header fields:
* (things start to be invalid below here) */
0x10, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x20, /* array key (this is not currently a valid header field) */
/* Variant array value: */
0x02, /* signature length */
'b', 'b', /* two complete types */
0x00, /* nul terminator */
/* (Variant array value payload missing) */
/* alignment padding before the next header array element, as structs must
* be 8-aligned: */
0x00, 0x00, 0x00,
0x05, /* array key (REPLY_SERIAL, required for method return messages) */
/* Variant array value: */
0x01, /* signature length */
'u', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x00, 0x01, 0x02, 0x03,
/* (message body is zero-length) */
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message);
g_clear_error (&local_error);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that an invalid header in a D-Bus message (specifically, containing a
* variant with a valid type signature that is too long to be a valid
* #GVariantType due to exceeding the array nesting limits) is gracefully
* handled with an error rather than a crash. */
static void
test_message_parse_over_long_signature_header (void)
{
const guint8 data[] = {
'l', /* little-endian byte order */
0x02, /* message type (method return) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0x00, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x20, 0x20, 0x20, 0x20, /* message serial */
/* a{yv} of header fields:
* (things start to be invalid below here) */
0xa0, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x08, /* array key (SIGNATURE) */
/* Variant array value: */
0x04, /* signature length */
'g', 0x00, 0x20, 0x20, /* one complete type plus some rubbish */
0x00, /* nul terminator */
/* (Variant array value payload) */
/* Critically, this contains 128 nested as, which exceeds
* %G_VARIANT_MAX_RECURSION_DEPTH. */
0xec,
'a', 'b', 'g', 'd', 'u', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
'd', 'd', 'd',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'v',
/* first header length is a multiple of 8 so no padding is needed */
0x05, /* array key (REPLY_SERIAL, required for method return messages) */
/* Variant array value: */
0x01, /* signature length */
'u', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x00, 0x01, 0x02, 0x03,
/* (message body is zero-length) */
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message);
g_clear_error (&local_error);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that an invalid header in a D-Bus message (specifically, containing too
* many levels of nested variant) is gracefully handled with an error rather
* than a crash. */
static void
test_message_parse_deep_header_nesting (void)
{
const guint8 data[] = {
'l', /* little-endian byte order */
0x02, /* message type (method return) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0x00, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x20, 0x20, 0x20, 0x20, /* message serial */
/* a{yv} of header fields:
* (things start to be invalid below here) */
0xd0, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x20, /* array key (this is not currently a valid header field) */
/* Variant array value: */
0x01, /* signature length */
'v', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
/* Critically, this contains 64 nested variants (minus two for the
* arbitrary valid content below, but ignoring two for the `a{yv}`
* above), which in total exceeds %G_DBUS_MAX_TYPE_DEPTH. */
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
/* Some arbitrary valid content inside the innermost variant: */
0x01, 'y', 0x00, 0xcc,
/* no padding needed as this header element length is a multiple of 8 */
0x05, /* array key (REPLY_SERIAL, required for method return messages) */
/* Variant array value: */
0x01, /* signature length */
'u', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x00, 0x01, 0x02, 0x03,
/* (message body is zero-length) */
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message);
g_clear_error (&local_error);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Test that an invalid body in a D-Bus message (specifically, containing too
* many levels of nested variant) is gracefully handled with an error rather
* than a crash. The set of bytes here are a modified version of the bytes from
* test_message_parse_deep_header_nesting(). */
static void
test_message_parse_deep_body_nesting (void)
{
const guint8 data[] = {
'l', /* little-endian byte order */
0x02, /* message type (method return) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0xc4, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x20, 0x20, 0x20, 0x20, /* message serial */
/* a{yv} of header fields: */
0x10, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x08, /* array key (SIGNATURE) */
/* Variant array value: */
0x01, /* signature length */
'g', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x01, 'v', 0x00,
/* alignment padding before the next header array element, as structs must
* be 8-aligned: */
0x00,
0x05, /* array key (REPLY_SERIAL, required for method return messages) */
/* Variant array value: */
0x01, /* signature length */
'u', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x00, 0x01, 0x02, 0x03,
/* Message body: over 64 levels of nested variant, which is not valid: */
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00, 0x01, 'v', 0x00,
/* Some arbitrary valid content inside the innermost variant: */
0x01, 'y', 0x00, 0xcc,
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message);
g_clear_error (&local_error);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
test_message_parse_truncated (void)
{
GDBusMessage *message = NULL;
GDBusMessage *message2 = NULL;
GVariantBuilder builder;
guchar *blob = NULL;
gsize size = 0;
GError *error = NULL;
g_test_summary ("Test that truncated messages are properly rejected.");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2528");
message = g_dbus_message_new ();
g_variant_builder_init_static (&builder, G_VARIANT_TYPE ("(asbynqiuxtd)"));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
g_variant_builder_add (&builder, "s", "fourtytwo");
g_variant_builder_close (&builder);
g_variant_builder_add (&builder, "b", TRUE);
g_variant_builder_add (&builder, "y", 42);
g_variant_builder_add (&builder, "n", 42);
g_variant_builder_add (&builder, "q", 42);
g_variant_builder_add (&builder, "i", 42);
g_variant_builder_add (&builder, "u", 42);
g_variant_builder_add (&builder, "x", 42);
g_variant_builder_add (&builder, "t", 42);
g_variant_builder_add (&builder, "d", (gdouble) 42);
g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH,
g_variant_new_object_path ("/foo/bar"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER,
g_variant_new_string ("Member"));
g_dbus_message_set_body (message, g_variant_builder_end (&builder));
blob = g_dbus_message_to_blob (message, &size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_no_error (error);
g_clear_object (&message);
/* Try parsing all possible prefixes of the full @blob. */
for (gsize i = 0; i < size; i++)
{
message2 = g_dbus_message_new_from_blob (blob, i, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message2);
g_clear_error (&error);
}
message2 = g_dbus_message_new_from_blob (blob, size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_no_error (error);
g_assert_true (G_IS_DBUS_MESSAGE (message2));
g_clear_object (&message2);
g_free (blob);
}
static void
test_message_parse_empty_structure (void)
{
const guint8 data[] =
{
'l', /* little-endian byte order */
0x02, /* message type (method return) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0x08, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x00, 0x00, 0x00, 0x00, /* message serial */
/* a{yv} of header fields */
0x20, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x01, /* array key (PATH) */
0x01, /* signature length */
'o', /* type (OBJECT_PATH) */
0x00, /* nul terminator */
0x05, 0x00, 0x00, 0x00, /* length 5 */
'/', 'p', 'a', 't', 'h', 0x00, 0x00, 0x00, /* string '/path' and padding */
0x03, /* array key (MEMBER) */
0x01, /* signature length */
's', /* type (STRING) */
0x00, /* nul terminator */
0x06, 0x00, 0x00, 0x00, /* length 6 */
'M', 'e', 'm', 'b', 'e', 'r', 0x00, 0x00, /* string 'Member' and padding */
0x08, /* array key (SIGNATURE) */
0x01, /* signature length */
'g', /* type (SIGNATURE) */
0x00, /* nul terminator */
0x03, /* length 3 */
'a', '(', ')', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* type 'a()' and padding */
0x08, 0x00, 0x00, 0x00, /* array length: 4 bytes */
0x00, 0x00, 0x00, 0x00, /* padding to 8 bytes */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* array data */
0x00
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
g_test_summary ("Test that empty structures are rejected when parsing.");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2557");
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (local_error->message, ==, "Empty structures (tuples) are not allowed in D-Bus");
g_assert_null (message);
g_clear_error (&local_error);
}
static void
test_message_serialize_empty_structure (void)
{
GDBusMessage *message;
GVariantBuilder builder;
gsize size = 0;
GError *local_error = NULL;
g_test_summary ("Test that empty structures are rejected when serializing.");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2557");
message = g_dbus_message_new ();
g_variant_builder_init_static (&builder, G_VARIANT_TYPE ("(a())"));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("a()"));
g_variant_builder_add (&builder, "()");
g_variant_builder_close (&builder);
g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH,
g_variant_new_object_path ("/path"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER,
g_variant_new_string ("Member"));
g_dbus_message_set_body (message, g_variant_builder_end (&builder));
g_dbus_message_to_blob (message, &size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (local_error->message, ==, "Empty structures (tuples) are not allowed in D-Bus");
g_clear_error (&local_error);
g_clear_object (&message);
}
static void
test_message_parse_missing_header (void)
{
const guint8 data[] = {
'l', /* little-endian byte order */
0x01, /* message type (method call) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0x12, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x20, 0x20, 0x20, 0x20, /* message serial */
/* a{yv} of header fields: */
0x24, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x01, /* array key (PATH, required for method call messages) */
/* Variant array value: */
0x01, /* signature length */
'o', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x01, 0x00, 0x00, 0x00,
'/', 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x30, /* array key (MEMBER, required for method call messages; CORRUPTED from 0x03) */
/* Variant array value: */
0x01, /* signature length */
's', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x03, 0x00, 0x00, 0x00,
'H', 'e', 'y', 0x00,
0x00, 0x00, 0x00, 0x00,
0x08, /* array key (SIGNATURE) */
/* Variant array value: */
0x01, /* signature length */
'g', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x02, 's', 's', 0x00,
/* Some arbitrary valid content inside the message body: */
0x03, 0x00, 0x00, 0x00,
'h', 'e', 'y', 0x00,
0x05, 0x00, 0x00, 0x00,
't', 'h', 'e', 'r', 'e', 0x00
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
g_test_summary ("Test that missing (required) headers prompt an error.");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3061");
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message);
g_clear_error (&local_error);
}
static void
test_message_parse_invalid_header_type (void)
{
const guint8 data[] = {
'l', /* little-endian byte order */
0x01, /* message type (method call) */
0x00, /* message flags (none) */
0x01, /* major protocol version */
0x12, 0x00, 0x00, 0x00, /* body length (in bytes) */
0x20, 0x20, 0x20, 0x20, /* message serial */
/* a{yv} of header fields: */
0x24, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
0x01, /* array key (PATH, required for method call messages) */
/* Variant array value: */
0x01, /* signature length */
'o', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x01, 0x00, 0x00, 0x00,
'/', 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x03, /* array key (MEMBER, required for method call messages) */
/* Variant array value: */
0x01, /* signature length */
't', /* one complete type; CORRUPTED, MEMBER should be 's' */
0x00, /* nul terminator */
/* (Padding to 64-bit alignment of 't)' */
0x00, 0x00, 0x00, 0x00,
/* (Variant array value payload) */
'H', 'e', 'y', 0x00,
0x00, 0x00, 0x00, 0x00,
0x08, /* array key (SIGNATURE) */
/* Variant array value: */
0x01, /* signature length */
'g', /* one complete type */
0x00, /* nul terminator */
/* (Variant array value payload) */
0x02, 's', 's', 0x00,
/* Some arbitrary valid content inside the message body: */
0x03, 0x00, 0x00, 0x00,
'h', 'e', 'y', 0x00,
0x05, 0x00, 0x00, 0x00,
't', 'h', 'e', 'r', 'e', 0x00
};
gsize size = sizeof (data);
GDBusMessage *message = NULL;
GError *local_error = NULL;
g_test_summary ("Test that the type of well-known headers is checked.");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3061");
message = g_dbus_message_new_from_blob ((guchar *) data, size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_null (message);
g_clear_error (&local_error);
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
g_setenv ("LC_ALL", "C", TRUE);
setlocale (LC_ALL, "C");
g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
g_test_add_func ("/gdbus/message-serialize/basic",
test_message_serialize_basic);
g_test_add_func ("/gdbus/message-serialize/complex",
test_message_serialize_complex);
g_test_add_func ("/gdbus/message-serialize/invalid",
test_message_serialize_invalid);
g_test_add_func ("/gdbus/message-serialize/header-checks",
test_message_serialize_header_checks);
g_test_add_func ("/gdbus/message-serialize/header-checks/valid",
test_message_serialize_header_checks_valid);
g_test_add_func ("/gdbus/message-serialize/double-array",
test_message_serialize_double_array);
g_test_add_func ("/gdbus/message-serialize/empty-structure",
test_message_serialize_empty_structure);
g_test_add_func ("/gdbus/message-parse/empty-arrays-of-arrays",
test_message_parse_empty_arrays_of_arrays);
g_test_add_func ("/gdbus/message-parse/non-signature-header",
test_message_parse_non_signature_header);
g_test_add_func ("/gdbus/message-parse/empty-signature-header",
test_message_parse_empty_signature_header);
g_test_add_func ("/gdbus/message-parse/multiple-signature-header",
test_message_parse_multiple_signature_header);
g_test_add_func ("/gdbus/message-parse/over-long-signature-header",
test_message_parse_over_long_signature_header);
g_test_add_func ("/gdbus/message-parse/deep-header-nesting",
test_message_parse_deep_header_nesting);
g_test_add_func ("/gdbus/message-parse/deep-body-nesting",
test_message_parse_deep_body_nesting);
g_test_add_func ("/gdbus/message-parse/truncated",
test_message_parse_truncated);
g_test_add_func ("/gdbus/message-parse/empty-structure",
test_message_parse_empty_structure);
g_test_add_func ("/gdbus/message-parse/missing-header",
test_message_parse_missing_header);
g_test_add_func ("/gdbus/message-parse/invalid-header-type",
test_message_parse_invalid_header_type);
return g_test_run();
}