glib/gio/tests/gdbus-serialization.c
Simon McVittie b4f8d4a5d5 gdbus: Use symbolic constants for the reserved Local path and interface
These are reserved by the D-Bus Specification.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2024-05-16 22:52:23 +01:00

1941 lines
70 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GLib testing framework examples and tests
*
* 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
* 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);
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;
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);
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"
"value 1: array:\n"
"value 2: array:\n"
" string: 'Something'\n");
g_variant_unref (value);
/* 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"
" 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 (&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 (&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 (&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();
}