mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-29 00:46:16 +01:00
f936bba0d1
These tests were originally written using the output directly from a fuzzer which had triggered the bugs we’re testing for. However, that means they’re liable to no longer test what they’re intended to test if the `GDBusMessage` parsing code is changed to (for example) check for certain errors earlier in future. It’s better to only have one invalidity in each binary blob, so change the test messages to all be valid apart from the specific thing they’re testing for. The changes were based on reading the D-Bus specification directly: https://dbus.freedesktop.org/doc/dbus-specification.html During these changes I found one problem in `test_message_parse_deep_header_nesting()` where it wasn’t actually nesting variants in the header deeply enough to trigger the bug it was supposed to be testing for. Fixed that. Signed-off-by: Philip Withnall <pwithnall@endlessos.org> Fixes: #1963
1504 lines
54 KiB
C
1504 lines
54 KiB
C
/* GLib testing framework examples and tests
|
||
*
|
||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||
*
|
||
* 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 *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;
|
||
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);
|
||
}
|
||
|
||
g_object_unref (message);
|
||
|
||
return g_steal_pointer (&s);
|
||
}
|
||
|
||
/* 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 that we can't serialize messages with SIGNATURE set to a non-signature-typed value
|
||
*/
|
||
message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember");
|
||
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, 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, ==, "Signature header found but is not of type signature");
|
||
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: PATH, INTERFACE or MEMBER header field is missing");
|
||
g_clear_error (&error);
|
||
g_assert_null (blob);
|
||
/* interface reserved value => error */
|
||
g_dbus_message_set_interface (message, "org.freedesktop.DBus.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: The INTERFACE header field is using the reserved value org.freedesktop.DBus.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, INTERFACE or MEMBER header field is missing");
|
||
g_clear_error (&error);
|
||
g_assert_null (blob);
|
||
/* path reserved value => error */
|
||
g_dbus_message_set_path (message, "/org/freedesktop/DBus/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: The PATH header field is using the reserved value /org/freedesktop/DBus/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: PATH, INTERFACE or MEMBER header field is missing");
|
||
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 or MEMBER header field is missing");
|
||
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: PATH or MEMBER header field is missing");
|
||
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");
|
||
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: REPLY_SERIAL or ERROR_NAME header field is missing");
|
||
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 or ERROR_NAME header field is missing");
|
||
g_clear_error (&error);
|
||
g_assert_null (blob);
|
||
g_object_unref (reply);
|
||
g_object_unref (message);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static void
|
||
test_message_parse_empty_arrays_of_arrays (void)
|
||
{
|
||
GVariant *body;
|
||
GError *error = NULL;
|
||
|
||
g_test_bug ("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 ("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 doesn’t match what’s 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 ‘a’s, 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);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
int
|
||
main (int argc,
|
||
char *argv[])
|
||
{
|
||
g_setenv ("LC_ALL", "C", TRUE);
|
||
setlocale (LC_ALL, "C");
|
||
|
||
g_test_init (&argc, &argv, NULL);
|
||
g_test_bug_base ("https://bugzilla.gnome.org/show_bug.cgi?id=");
|
||
|
||
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/double-array",
|
||
test_message_serialize_double_array);
|
||
|
||
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);
|
||
|
||
return g_test_run();
|
||
}
|