mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-01-06 22:20:20 +01:00
Add a test for header fields
Signed-off-by: Simon McVittie <smcv@collabora.com> Bug: https://bugs.freedesktop.org/show_bug.cgi?id=100317 Reviewed-by: Philip Withnall <withnall@endlessm.com> Signed-off-by: Simon McVittie <smcv@collabora.com>
This commit is contained in:
parent
cfd73beacf
commit
6f8d82009d
5 changed files with 783 additions and 0 deletions
|
|
@ -612,6 +612,9 @@ _dbus_header_create (DBusHeader *header,
|
|||
goto oom;
|
||||
}
|
||||
|
||||
/* Note that test/message.c relies on this being in the middle of the
|
||||
* message: if you change the order of serialization here (but why
|
||||
* would you?), please find some other way to retain test coverage. */
|
||||
if (interface != NULL)
|
||||
{
|
||||
if (!write_basic_field (&array,
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ DBUS_PRIVATE_EXPORT
|
|||
int _dbus_type_reader_get_current_type (const DBusTypeReader *reader);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
int _dbus_type_reader_get_element_type (const DBusTypeReader *reader);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
int _dbus_type_reader_get_value_pos (const DBusTypeReader *reader);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
void _dbus_type_reader_read_basic (const DBusTypeReader *reader,
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ DBUS_PRIVATE_EXPORT
|
|||
void _dbus_string_init_const_len (DBusString *str,
|
||||
const char *value,
|
||||
int len);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
dbus_bool_t _dbus_string_init_preallocated (DBusString *str,
|
||||
int allocate_size);
|
||||
|
||||
|
|
@ -112,6 +113,7 @@ char* _dbus_string_get_data (DBusString *str);
|
|||
DBUS_PRIVATE_EXPORT
|
||||
const char* _dbus_string_get_const_data (const DBusString *str);
|
||||
#endif /* _dbus_string_get_const_data */
|
||||
DBUS_PRIVATE_EXPORT
|
||||
char* _dbus_string_get_data_len (DBusString *str,
|
||||
int start,
|
||||
int len);
|
||||
|
|
@ -226,6 +228,7 @@ dbus_bool_t _dbus_string_insert_2_aligned (DBusString *str,
|
|||
dbus_bool_t _dbus_string_insert_4_aligned (DBusString *str,
|
||||
int insert_at,
|
||||
const unsigned char octets[4]);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
dbus_bool_t _dbus_string_insert_8_aligned (DBusString *str,
|
||||
int insert_at,
|
||||
const unsigned char octets[8]);
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ installable_tests += \
|
|||
test-dbus-daemon \
|
||||
test-dbus-daemon-eavesdrop \
|
||||
test-fdpass \
|
||||
test-header-fields \
|
||||
test-message \
|
||||
test-monitor \
|
||||
test-loopback \
|
||||
|
|
@ -324,6 +325,15 @@ test_dbus_daemon_eavesdrop_LDADD = \
|
|||
$(GLIB_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
test_header_fields_SOURCES = \
|
||||
header-fields.c \
|
||||
$(NULL)
|
||||
test_header_fields_LDADD = \
|
||||
libdbus-testutils.la \
|
||||
$(top_builddir)/dbus/libdbus-internal.la \
|
||||
$(GLIB_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
if DBUS_UNIX
|
||||
test_sd_activation_SOURCES = \
|
||||
sd-activation.c \
|
||||
|
|
|
|||
766
test/header-fields.c
Normal file
766
test/header-fields.c
Normal file
|
|
@ -0,0 +1,766 @@
|
|||
/* Unit tests for detailed header field manipulation
|
||||
*
|
||||
* Copyright © 2017 Collabora Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include "dbus/dbus-internals.h"
|
||||
#include "dbus/dbus-marshal-recursive.h"
|
||||
#include "dbus/dbus-message-private.h"
|
||||
#include "dbus/dbus-string.h"
|
||||
#include "dbus/dbus-test-tap.h"
|
||||
#include "test-utils-glib.h"
|
||||
|
||||
typedef struct {
|
||||
const gchar *mode;
|
||||
TestMainContext *ctx;
|
||||
DBusConnection *left_conn;
|
||||
DBusConnection *right_conn;
|
||||
GPid daemon_pid;
|
||||
gchar *address;
|
||||
GQueue held_messages;
|
||||
gboolean skip;
|
||||
} Fixture;
|
||||
|
||||
static DBusHandlerResult
|
||||
hold_filter (DBusConnection *connection,
|
||||
DBusMessage *message,
|
||||
void *user_data)
|
||||
{
|
||||
Fixture *f = user_data;
|
||||
|
||||
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
g_queue_push_tail (&f->held_messages, dbus_message_ref (message));
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_dbus_daemon (Fixture *f,
|
||||
gconstpointer context G_GNUC_UNUSED)
|
||||
{
|
||||
f->address = test_get_dbus_daemon (NULL, TEST_USER_ME, NULL, &f->daemon_pid);
|
||||
|
||||
if (f->address == NULL)
|
||||
{
|
||||
f->skip = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
teardown_dbus_daemon (Fixture *f,
|
||||
gconstpointer context G_GNUC_UNUSED)
|
||||
{
|
||||
if (f->daemon_pid != 0)
|
||||
{
|
||||
test_kill_pid (f->daemon_pid);
|
||||
g_spawn_close_pid (f->daemon_pid);
|
||||
f->daemon_pid = 0;
|
||||
}
|
||||
|
||||
g_clear_pointer (&f->address, g_free);
|
||||
}
|
||||
|
||||
/* Offset to byte order from start of header */
|
||||
#define BYTE_ORDER_OFFSET 0
|
||||
/* Offset to version from start of header */
|
||||
#define VERSION_OFFSET 3
|
||||
/* Offset to fields array length from start of header, in protocol v1 */
|
||||
#define FIELDS_ARRAY_LENGTH_OFFSET 12
|
||||
/** Offset to first field in header, in protocol v1 */
|
||||
#define FIRST_FIELD_OFFSET 16
|
||||
|
||||
/* Offset from start of _dbus_header_signature_str to the signature of
|
||||
* the fields array */
|
||||
#define FIELDS_ARRAY_SIGNATURE_OFFSET 6
|
||||
|
||||
/* A byte that is not a DBUS_HEADER_FIELD_* */
|
||||
#define NOT_A_HEADER_FIELD 123
|
||||
|
||||
_DBUS_STRING_DEFINE_STATIC(_dbus_header_signature_str, DBUS_HEADER_SIGNATURE);
|
||||
|
||||
static void
|
||||
string_overwrite_n (DBusString *str,
|
||||
int start,
|
||||
const void *bytes,
|
||||
int len)
|
||||
{
|
||||
unsigned char *data = _dbus_string_get_udata_len (str, start, len);
|
||||
|
||||
g_assert (data != NULL);
|
||||
memcpy (data, bytes, len);
|
||||
}
|
||||
|
||||
static void
|
||||
steal_reply_cb (DBusPendingCall *pc,
|
||||
void *data)
|
||||
{
|
||||
DBusMessage **message_p = data;
|
||||
|
||||
g_assert (message_p != NULL);
|
||||
g_assert (*message_p == NULL);
|
||||
*message_p = dbus_pending_call_steal_reply (pc);
|
||||
g_assert (*message_p != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the handling of unknown header fields.
|
||||
*
|
||||
* Return TRUE if the right thing happens, but the right thing might include
|
||||
* OOM.
|
||||
*/
|
||||
static dbus_bool_t
|
||||
test_weird_header_field (void *user_data,
|
||||
dbus_bool_t have_memory)
|
||||
{
|
||||
Fixture *f = user_data;
|
||||
const char *body = "hello";
|
||||
const char *new_body = NULL;
|
||||
DBusError error = DBUS_ERROR_INIT;
|
||||
DBusMessage *original = NULL;
|
||||
DBusMessage *modified = NULL;
|
||||
DBusMessage *filtered = NULL;
|
||||
DBusMessage *relayed = NULL;
|
||||
DBusMessage *reply = NULL;
|
||||
DBusPendingCall *pc = NULL;
|
||||
char *blob = NULL;
|
||||
int blob_len;
|
||||
DBusString modified_blob = _DBUS_STRING_INIT_INVALID;
|
||||
/* This is the serialization of a struct (uv), assumed to be at an
|
||||
* 8-byte boundary. */
|
||||
const unsigned char weird_header[8] = {
|
||||
NOT_A_HEADER_FIELD, /*< type code */
|
||||
1, /*< length of signature */
|
||||
'u', /*< signature: uint32 */
|
||||
'\0', /*< end of signature */
|
||||
/* no padding required */
|
||||
'\x12', /*< uint32 0x12345678 (BE) or 0x78563412 (LE) */
|
||||
'\x34',
|
||||
'\x56',
|
||||
'\x78'
|
||||
};
|
||||
int bytes_needed;
|
||||
DBusTypeReader reader;
|
||||
DBusTypeReader array;
|
||||
gboolean added_hold_filter = FALSE;
|
||||
GError *gerror = NULL;
|
||||
|
||||
if (f->skip)
|
||||
return TRUE;
|
||||
|
||||
/* We'd normally do this in setup_dbus_daemon(), but then we couldn't
|
||||
* allocate memory that we want freed by dbus_shutdown(). */
|
||||
g_assert_cmpint (_dbus_get_malloc_blocks_outstanding (), ==, 0);
|
||||
f->ctx = test_main_context_try_get ();
|
||||
|
||||
if (f->ctx == NULL)
|
||||
{
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
f->left_conn = test_try_connect_to_bus (f->ctx, f->address, &gerror);
|
||||
|
||||
if (f->left_conn == NULL)
|
||||
{
|
||||
g_assert_error (gerror, G_DBUS_ERROR, G_DBUS_ERROR_NO_MEMORY);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
f->right_conn = test_try_connect_to_bus (f->ctx, f->address, &gerror);
|
||||
|
||||
if (f->right_conn == NULL)
|
||||
{
|
||||
g_assert_error (gerror, G_DBUS_ERROR, G_DBUS_ERROR_NO_MEMORY);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dbus_connection_add_filter (f->right_conn, hold_filter, f, NULL))
|
||||
{
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
added_hold_filter = TRUE;
|
||||
|
||||
original = dbus_message_new_method_call (dbus_bus_get_unique_name (f->right_conn),
|
||||
"/com/example/Path",
|
||||
"com.example.Interface",
|
||||
"Method");
|
||||
|
||||
if (original == NULL ||
|
||||
!dbus_message_append_args (original,
|
||||
DBUS_TYPE_STRING, &body,
|
||||
DBUS_TYPE_INVALID))
|
||||
{
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Messages with serial number 0 can't be demarshalled. */
|
||||
dbus_message_set_serial (original, 42);
|
||||
|
||||
if (!dbus_message_marshal (original, &blob, &blob_len))
|
||||
{
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We will add up to 8 bytes, so preallocate that much. */
|
||||
if (!_dbus_string_init_preallocated (&modified_blob, blob_len + 8) ||
|
||||
!_dbus_string_append_len (&modified_blob, blob, blob_len))
|
||||
{
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If these are false then we need to change our byte-twiddling */
|
||||
g_assert_cmpint (blob_len, >, FIRST_FIELD_OFFSET);
|
||||
g_assert_cmpint (blob[VERSION_OFFSET], ==, 1);
|
||||
g_assert_cmpint (blob[BYTE_ORDER_OFFSET], ==, DBUS_COMPILER_BYTE_ORDER);
|
||||
|
||||
if (f->mode == NULL)
|
||||
{
|
||||
/* Do nothing: don't insert a weird header field at all */
|
||||
}
|
||||
else if (g_str_equal (f->mode, "change"))
|
||||
{
|
||||
/* Replace the interface (which is optional anyway) with the
|
||||
* weird header field */
|
||||
|
||||
_dbus_type_reader_init (&reader, DBUS_COMPILER_BYTE_ORDER,
|
||||
&_dbus_header_signature_str,
|
||||
FIELDS_ARRAY_SIGNATURE_OFFSET,
|
||||
&modified_blob,
|
||||
FIELDS_ARRAY_LENGTH_OFFSET);
|
||||
_dbus_type_reader_recurse (&reader, &array);
|
||||
|
||||
while (_dbus_type_reader_get_current_type (&array) !=
|
||||
DBUS_TYPE_INVALID)
|
||||
{
|
||||
DBusTypeReader sub;
|
||||
unsigned char field_code;
|
||||
|
||||
_dbus_type_reader_recurse (&array, &sub);
|
||||
|
||||
g_assert_cmpint (_dbus_type_reader_get_current_type (&sub),
|
||||
==, DBUS_TYPE_BYTE);
|
||||
_dbus_type_reader_read_basic (&sub, &field_code);
|
||||
|
||||
if (field_code == DBUS_HEADER_FIELD_INTERFACE)
|
||||
{
|
||||
_dbus_string_set_byte (&modified_blob,
|
||||
_dbus_type_reader_get_value_pos (&sub),
|
||||
NOT_A_HEADER_FIELD);
|
||||
break;
|
||||
}
|
||||
|
||||
_dbus_type_reader_next (&array);
|
||||
}
|
||||
}
|
||||
else if (g_str_equal (f->mode, "prepend"))
|
||||
{
|
||||
dbus_uint32_t header_fields_length;
|
||||
|
||||
memcpy (&header_fields_length, &blob[FIELDS_ARRAY_LENGTH_OFFSET], 4);
|
||||
|
||||
/* Insert a weird header field at the beginning of the fields
|
||||
* array. We do this by byte manipulation rather than by using a
|
||||
* DBusTypeReader, because we eventually want to get rid of the
|
||||
* counterintuitive ability for a DBusTypeReader to write to the
|
||||
* message: https://bugs.freedesktop.org/show_bug.cgi?id=38288 */
|
||||
if (!_dbus_string_insert_8_aligned (&modified_blob,
|
||||
FIRST_FIELD_OFFSET,
|
||||
weird_header))
|
||||
{
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
header_fields_length += 8;
|
||||
string_overwrite_n (&modified_blob, FIELDS_ARRAY_LENGTH_OFFSET,
|
||||
&header_fields_length, 4);
|
||||
}
|
||||
else if (g_str_equal (f->mode, "append"))
|
||||
{
|
||||
dbus_uint32_t header_fields_length;
|
||||
|
||||
memcpy (&header_fields_length, &blob[FIELDS_ARRAY_LENGTH_OFFSET], 4);
|
||||
|
||||
/* Insert a weird header field at the end of the fields
|
||||
* array, after the padding (which was previously the padding between
|
||||
* header and body, and is now the padding between the last-but-one
|
||||
* header field and the new header field). For simplicity, we've
|
||||
* used a weird header field that does not ever need to be followed
|
||||
* by padding itself.
|
||||
*
|
||||
* Old:
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
* | ... 16-byte fixed-length part ... |
|
||||
* | ... 16-byte fixed-length part ... |
|
||||
* [A] first header field ... |
|
||||
* | last header field [B] padding [C]
|
||||
* [C] body...
|
||||
*
|
||||
* New:
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
* | ... 16-byte fixed-length part ... |
|
||||
* | ... 16-byte fixed-length part ... |
|
||||
* [A] first header field ... |
|
||||
* | previously-last field [B] padding [C]
|
||||
* | weird header field, exactly 8 bytes long [B'=C']
|
||||
* [B'=C'] body...
|
||||
*
|
||||
*/
|
||||
header_fields_length = _DBUS_ALIGN_VALUE (header_fields_length, 8);
|
||||
g_assert_cmpint (header_fields_length % 8, ==, 0);
|
||||
|
||||
if (!_dbus_string_insert_8_aligned (&modified_blob,
|
||||
(FIRST_FIELD_OFFSET +
|
||||
header_fields_length),
|
||||
weird_header))
|
||||
{
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
header_fields_length += 8;
|
||||
string_overwrite_n (&modified_blob, FIELDS_ARRAY_LENGTH_OFFSET,
|
||||
&header_fields_length, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
/* OK, now we've hacked up the message, compare it with the original. */
|
||||
bytes_needed = dbus_message_demarshal_bytes_needed (_dbus_string_get_const_data (&modified_blob),
|
||||
_dbus_string_get_length (&modified_blob));
|
||||
|
||||
if (f->mode == NULL || g_str_equal (f->mode, "change"))
|
||||
{
|
||||
/* We edited the message in-place so its effective length didn't
|
||||
* change */
|
||||
g_assert_cmpint (bytes_needed, ==, blob_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_cmpint (bytes_needed, ==, blob_len + 8);
|
||||
}
|
||||
|
||||
g_assert_cmpint (_dbus_string_get_length (&modified_blob), ==,
|
||||
bytes_needed);
|
||||
modified = dbus_message_demarshal (_dbus_string_get_const_data (&modified_blob),
|
||||
_dbus_string_get_length (&modified_blob),
|
||||
&error);
|
||||
|
||||
if (modified == NULL)
|
||||
{
|
||||
g_assert_cmpstr (error.name, ==, DBUS_ERROR_NO_MEMORY);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The modified message has the same fields, except possibly the
|
||||
* interface. */
|
||||
g_assert_cmpint (dbus_message_get_type (modified), ==,
|
||||
dbus_message_get_type (original));
|
||||
g_assert_cmpstr (dbus_message_get_path (modified), ==,
|
||||
dbus_message_get_path (original));
|
||||
g_assert_cmpstr (dbus_message_get_member (modified), ==,
|
||||
dbus_message_get_member (original));
|
||||
g_assert_cmpstr (dbus_message_get_error_name (modified), ==,
|
||||
dbus_message_get_error_name (original));
|
||||
g_assert_cmpstr (dbus_message_get_destination (modified), ==,
|
||||
dbus_message_get_destination (original));
|
||||
g_assert_cmpstr (dbus_message_get_sender (modified), ==,
|
||||
dbus_message_get_sender (original));
|
||||
g_assert_cmpstr (dbus_message_get_signature (modified), ==,
|
||||
dbus_message_get_signature (original));
|
||||
g_assert_cmpint (dbus_message_get_no_reply (modified), ==,
|
||||
dbus_message_get_no_reply (original));
|
||||
g_assert_cmpint (dbus_message_get_serial (modified), ==,
|
||||
dbus_message_get_serial (original));
|
||||
g_assert_cmpint (dbus_message_get_reply_serial (modified), ==,
|
||||
dbus_message_get_reply_serial (original));
|
||||
g_assert_cmpint (dbus_message_get_auto_start (modified), ==,
|
||||
dbus_message_get_auto_start (original));
|
||||
g_assert_cmpint (dbus_message_get_allow_interactive_authorization (modified),
|
||||
==,
|
||||
dbus_message_get_allow_interactive_authorization (original));
|
||||
|
||||
if (dbus_message_get_args (modified, &error,
|
||||
DBUS_TYPE_STRING, &new_body,
|
||||
DBUS_TYPE_INVALID))
|
||||
{
|
||||
g_assert_cmpstr (new_body, ==, body);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_cmpstr (error.name, ==, DBUS_ERROR_NO_MEMORY);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (f->mode != NULL && g_str_equal (f->mode, "change"))
|
||||
{
|
||||
/* We edited the interface field in-place to turn it into the
|
||||
* unknown field, so it doesn't have an interface any more. */
|
||||
g_assert_cmpstr (dbus_message_get_interface (modified), ==, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We didn't change the interface. */
|
||||
g_assert_cmpstr (dbus_message_get_interface (modified), ==,
|
||||
dbus_message_get_interface (original));
|
||||
}
|
||||
|
||||
/* Copy the modified message so we can filter it. */
|
||||
filtered = dbus_message_demarshal (_dbus_string_get_const_data (&modified_blob),
|
||||
_dbus_string_get_length (&modified_blob),
|
||||
&error);
|
||||
|
||||
if (filtered == NULL)
|
||||
{
|
||||
g_assert_cmpstr (error.name, ==, DBUS_ERROR_NO_MEMORY);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* TODO: Actually filter its header */
|
||||
|
||||
/* All known headers are the same as in the modified message that was
|
||||
* deserialized from the same blob */
|
||||
g_assert_cmpint (dbus_message_get_type (filtered), ==,
|
||||
dbus_message_get_type (modified));
|
||||
g_assert_cmpstr (dbus_message_get_path (filtered), ==,
|
||||
dbus_message_get_path (modified));
|
||||
g_assert_cmpstr (dbus_message_get_member (filtered), ==,
|
||||
dbus_message_get_member (modified));
|
||||
g_assert_cmpstr (dbus_message_get_error_name (filtered), ==,
|
||||
dbus_message_get_error_name (modified));
|
||||
g_assert_cmpstr (dbus_message_get_destination (filtered), ==,
|
||||
dbus_message_get_destination (modified));
|
||||
g_assert_cmpstr (dbus_message_get_sender (filtered), ==,
|
||||
dbus_message_get_sender (modified));
|
||||
g_assert_cmpstr (dbus_message_get_signature (filtered), ==,
|
||||
dbus_message_get_signature (modified));
|
||||
g_assert_cmpint (dbus_message_get_no_reply (filtered), ==,
|
||||
dbus_message_get_no_reply (modified));
|
||||
g_assert_cmpint (dbus_message_get_serial (filtered), ==,
|
||||
dbus_message_get_serial (modified));
|
||||
g_assert_cmpint (dbus_message_get_reply_serial (filtered), ==,
|
||||
dbus_message_get_reply_serial (modified));
|
||||
g_assert_cmpint (dbus_message_get_auto_start (filtered), ==,
|
||||
dbus_message_get_auto_start (modified));
|
||||
g_assert_cmpint (dbus_message_get_allow_interactive_authorization (modified),
|
||||
==,
|
||||
dbus_message_get_allow_interactive_authorization (original));
|
||||
|
||||
/* The body is also the same */
|
||||
if (dbus_message_get_args (filtered, &error,
|
||||
DBUS_TYPE_STRING, &new_body,
|
||||
DBUS_TYPE_INVALID))
|
||||
{
|
||||
g_assert_cmpstr (new_body, ==, body);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_cmpstr (error.name, ==, DBUS_ERROR_NO_MEMORY);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We can't use _dbus_header_get_field_raw() because it asserts that
|
||||
* the field in question is in range. */
|
||||
_dbus_type_reader_init (&reader, DBUS_COMPILER_BYTE_ORDER,
|
||||
&_dbus_header_signature_str,
|
||||
FIELDS_ARRAY_SIGNATURE_OFFSET,
|
||||
&filtered->header.data,
|
||||
FIELDS_ARRAY_LENGTH_OFFSET);
|
||||
_dbus_type_reader_recurse (&reader, &array);
|
||||
|
||||
while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
|
||||
{
|
||||
DBusTypeReader sub;
|
||||
unsigned char field_code;
|
||||
|
||||
_dbus_type_reader_recurse (&array, &sub);
|
||||
|
||||
g_assert_cmpint (_dbus_type_reader_get_current_type (&sub),
|
||||
==, DBUS_TYPE_BYTE);
|
||||
_dbus_type_reader_read_basic (&sub, &field_code);
|
||||
|
||||
if (field_code == NOT_A_HEADER_FIELD)
|
||||
{
|
||||
g_test_message ("TODO: header field was not filtered out");
|
||||
}
|
||||
|
||||
_dbus_type_reader_next (&array);
|
||||
}
|
||||
|
||||
/* Sending a message through the dbus-daemon currently preserves
|
||||
* unknown header fields, but it should not.
|
||||
* https://bugs.freedesktop.org/show_bug.cgi?id=100317 */
|
||||
|
||||
/* We have to use send_with_reply because if we don't, we won't know
|
||||
* if the message was dropped on the floor due to out-of-memory. */
|
||||
if (!dbus_connection_send_with_reply (f->left_conn, modified, &pc, -1))
|
||||
{
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dbus_pending_call_get_completed (pc))
|
||||
{
|
||||
steal_reply_cb (pc, &reply);
|
||||
}
|
||||
else if (!dbus_pending_call_set_notify (pc, steal_reply_cb, &reply, NULL))
|
||||
{
|
||||
dbus_pending_call_cancel (pc);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (g_queue_get_length (&f->held_messages) < 1 && reply == NULL)
|
||||
test_main_context_iterate (f->ctx, TRUE);
|
||||
|
||||
if (reply != NULL)
|
||||
{
|
||||
dbus_set_error_from_message (&error, reply);
|
||||
g_assert_cmpstr (error.name, ==, DBUS_ERROR_NO_MEMORY);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
relayed = g_queue_pop_head (&f->held_messages);
|
||||
g_assert_cmpint (g_queue_get_length (&f->held_messages), ==, 0);
|
||||
|
||||
/* The message relayed through the dbus-daemon has the same known
|
||||
* fields and content as the one we sent, except that it has a sender
|
||||
* and a serial number. */
|
||||
g_assert_cmpint (dbus_message_get_type (relayed), ==,
|
||||
dbus_message_get_type (modified));
|
||||
g_assert_cmpstr (dbus_message_get_path (relayed), ==,
|
||||
dbus_message_get_path (modified));
|
||||
g_assert_cmpstr (dbus_message_get_member (relayed), ==,
|
||||
dbus_message_get_member (modified));
|
||||
g_assert_cmpstr (dbus_message_get_error_name (relayed), ==,
|
||||
dbus_message_get_error_name (modified));
|
||||
g_assert_cmpstr (dbus_message_get_destination (relayed), ==,
|
||||
dbus_message_get_destination (modified));
|
||||
g_assert_cmpstr (dbus_message_get_sender (relayed), ==,
|
||||
dbus_bus_get_unique_name (f->left_conn));
|
||||
g_assert_cmpstr (dbus_message_get_signature (relayed), ==,
|
||||
dbus_message_get_signature (modified));
|
||||
g_assert_cmpint (dbus_message_get_no_reply (relayed), ==,
|
||||
dbus_message_get_no_reply (modified));
|
||||
g_assert_cmpint (dbus_message_get_serial (relayed), !=, 0);
|
||||
g_assert_cmpint (dbus_message_get_reply_serial (relayed), ==,
|
||||
dbus_message_get_reply_serial (modified));
|
||||
g_assert_cmpint (dbus_message_get_auto_start (relayed), ==,
|
||||
dbus_message_get_auto_start (modified));
|
||||
g_assert_cmpint (dbus_message_get_allow_interactive_authorization (modified),
|
||||
==,
|
||||
dbus_message_get_allow_interactive_authorization (original));
|
||||
|
||||
if (dbus_message_get_args (relayed, &error,
|
||||
DBUS_TYPE_STRING, &new_body,
|
||||
DBUS_TYPE_INVALID))
|
||||
{
|
||||
g_assert_cmpstr (new_body, ==, body);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_cmpstr (error.name, ==, DBUS_ERROR_NO_MEMORY);
|
||||
g_assert_false (have_memory);
|
||||
goto out;
|
||||
}
|
||||
|
||||
_dbus_type_reader_init (&reader, DBUS_COMPILER_BYTE_ORDER,
|
||||
&_dbus_header_signature_str,
|
||||
FIELDS_ARRAY_SIGNATURE_OFFSET,
|
||||
&relayed->header.data,
|
||||
FIELDS_ARRAY_LENGTH_OFFSET);
|
||||
_dbus_type_reader_recurse (&reader, &array);
|
||||
|
||||
while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
|
||||
{
|
||||
DBusTypeReader sub;
|
||||
unsigned char field_code;
|
||||
|
||||
_dbus_type_reader_recurse (&array, &sub);
|
||||
|
||||
g_assert_cmpint (_dbus_type_reader_get_current_type (&sub),
|
||||
==, DBUS_TYPE_BYTE);
|
||||
_dbus_type_reader_read_basic (&sub, &field_code);
|
||||
|
||||
if (field_code == NOT_A_HEADER_FIELD)
|
||||
{
|
||||
g_test_message ("TODO: header field was passed through");
|
||||
}
|
||||
|
||||
_dbus_type_reader_next (&array);
|
||||
}
|
||||
|
||||
/* On success, we don't actually reply: it's one more thing that
|
||||
* could hit OOM */
|
||||
|
||||
out:
|
||||
g_clear_error (&gerror);
|
||||
_dbus_string_free (&modified_blob);
|
||||
dbus_free (blob);
|
||||
|
||||
if (pc != NULL)
|
||||
dbus_pending_call_cancel (pc);
|
||||
|
||||
dbus_clear_pending_call (&pc);
|
||||
dbus_clear_message (&reply);
|
||||
dbus_clear_message (&relayed);
|
||||
dbus_clear_message (&filtered);
|
||||
dbus_clear_message (&modified);
|
||||
dbus_clear_message (&original);
|
||||
dbus_error_free (&error);
|
||||
|
||||
/* We'd normally do this in teardown_dbus_daemon(), but for this test
|
||||
* we want to do it here so we can dbus_shutdown(). */
|
||||
if (f->left_conn != NULL)
|
||||
{
|
||||
dbus_connection_close (f->left_conn);
|
||||
test_connection_shutdown (f->ctx, f->left_conn);
|
||||
}
|
||||
|
||||
if (f->right_conn != NULL)
|
||||
{
|
||||
if (added_hold_filter)
|
||||
dbus_connection_remove_filter (f->right_conn, hold_filter, f);
|
||||
|
||||
g_queue_foreach (&f->held_messages, (GFunc) dbus_message_unref, NULL);
|
||||
g_queue_clear (&f->held_messages);
|
||||
|
||||
dbus_connection_close (f->right_conn);
|
||||
test_connection_shutdown (f->ctx, f->right_conn);
|
||||
}
|
||||
|
||||
dbus_clear_connection (&f->left_conn);
|
||||
dbus_clear_connection (&f->right_conn);
|
||||
g_clear_pointer (&f->ctx, test_main_context_unref);
|
||||
|
||||
dbus_shutdown ();
|
||||
g_assert_cmpint (_dbus_get_malloc_blocks_outstanding (), ==, 0);
|
||||
|
||||
return !g_test_failed ();
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *name;
|
||||
DBusTestMemoryFunction function;
|
||||
const gchar *mode;
|
||||
} OOMTestCase;
|
||||
|
||||
static void
|
||||
test_oom_wrapper (Fixture *f,
|
||||
gconstpointer data)
|
||||
{
|
||||
const OOMTestCase *test = data;
|
||||
|
||||
f->mode = test->mode;
|
||||
|
||||
if (g_test_slow ())
|
||||
{
|
||||
/* When we say slow, we mean it. */
|
||||
test_timeout_reset (30);
|
||||
}
|
||||
else
|
||||
{
|
||||
test_timeout_reset (1);
|
||||
}
|
||||
|
||||
if (!_dbus_test_oom_handling (test->name, test->function, f))
|
||||
{
|
||||
g_test_message ("OOM test failed");
|
||||
g_test_fail ();
|
||||
}
|
||||
}
|
||||
|
||||
static GQueue *test_cases_to_free = NULL;
|
||||
|
||||
static void
|
||||
add_oom_test (const gchar *name,
|
||||
DBusTestMemoryFunction function,
|
||||
const gchar *mode)
|
||||
{
|
||||
/* By using GLib memory allocation here, we avoid being affected by
|
||||
* dbus_shutdown() or contributing to
|
||||
* _dbus_get_malloc_blocks_outstanding() */
|
||||
OOMTestCase *test_case = g_new0 (OOMTestCase, 1);
|
||||
|
||||
test_case->name = name;
|
||||
test_case->function = function;
|
||||
test_case->mode = mode;
|
||||
g_test_add (name, Fixture, test_case, setup_dbus_daemon,
|
||||
test_oom_wrapper, teardown_dbus_daemon);
|
||||
g_queue_push_tail (test_cases_to_free, test_case);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
test_init (&argc, &argv);
|
||||
|
||||
/* Normally we test up to 4 consecutive malloc failures, but that's
|
||||
* painfully slow here. */
|
||||
if (g_getenv ("DBUS_TEST_MALLOC_FAILURES") == NULL)
|
||||
{
|
||||
if (!g_test_slow ())
|
||||
g_setenv ("DBUS_TEST_MALLOC_FAILURES", "2", TRUE);
|
||||
}
|
||||
|
||||
test_cases_to_free = g_queue_new ();
|
||||
add_oom_test ("/message/weird-header-field/none", test_weird_header_field,
|
||||
NULL);
|
||||
add_oom_test ("/message/weird-header-field/append", test_weird_header_field,
|
||||
"append");
|
||||
add_oom_test ("/message/weird-header-field/change", test_weird_header_field,
|
||||
"change");
|
||||
add_oom_test ("/message/weird-header-field/prepend", test_weird_header_field,
|
||||
"prepend");
|
||||
|
||||
ret = g_test_run ();
|
||||
|
||||
g_queue_free_full (test_cases_to_free, g_free);
|
||||
return ret;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue