diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/Makefile.am | 8 | ||||
-rw-r--r-- | test/data/valid-config-files/.gitignore | 1 | ||||
-rw-r--r-- | test/data/valid-config-files/finite-timeout.conf.in | 19 | ||||
-rw-r--r-- | test/dbus-daemon.c | 192 | ||||
-rw-r--r-- | test/fdpass.c | 853 | ||||
-rw-r--r-- | test/internals/syslog.c | 4 | ||||
-rw-r--r-- | test/manual-dir-iter.c | 92 | ||||
-rw-r--r-- | test/name-test/test-threads-init.c | 10 |
8 files changed, 1164 insertions, 15 deletions
diff --git a/test/Makefile.am b/test/Makefile.am index cec5cdab..173df74b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -75,12 +75,12 @@ TEST_BINARIES = \ ## since they depend on stuff from this directory TESTS = \ ../bus/test-bus$(EXEEXT) \ - ../bus/test-bus-system$(EXEEXT) \ ../dbus/test-dbus$(EXEEXT) \ $(NULL) if DBUS_UNIX TESTS += ../bus/test-bus-launch-helper$(EXEEXT) +TESTS += ../bus/test-bus-system$(EXEEXT) endif else !DBUS_ENABLE_EMBEDDED_TESTS @@ -119,6 +119,10 @@ test_syslog_SOURCES = internals/syslog.c test_syslog_CPPFLAGS = $(static_cppflags) test_syslog_LDADD = libdbus-testutils-internal.la $(GLIB_LIBS) +manual_dir_iter_SOURCES = manual-dir-iter.c +manual_dir_iter_CPPFLAGS = $(static_cppflags) +manual_dir_iter_LDADD = $(top_builddir)/dbus/libdbus-internal.la + EXTRA_DIST = dbus-test-runner testexecdir = $(libdir)/dbus-1.0/test @@ -130,6 +134,7 @@ installable_tests = \ test-printf \ $(NULL) installable_manual_tests = \ + manual-dir-iter \ $(NULL) if DBUS_WITH_GLIB @@ -249,6 +254,7 @@ in_data = \ data/valid-config-files-system/debug-allow-all-pass.conf.in \ data/valid-config-files/debug-allow-all-sha1.conf.in \ data/valid-config-files/debug-allow-all.conf.in \ + data/valid-config-files/finite-timeout.conf.in \ data/valid-config-files/incoming-limit.conf.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service.in \ diff --git a/test/data/valid-config-files/.gitignore b/test/data/valid-config-files/.gitignore index a38e9d15..8b16257f 100644 --- a/test/data/valid-config-files/.gitignore +++ b/test/data/valid-config-files/.gitignore @@ -1,5 +1,6 @@ debug-allow-all.conf debug-allow-all-sha1.conf +incoming-limit.conf session.conf system.conf run-with-tmp-session-bus.conf diff --git a/test/data/valid-config-files/finite-timeout.conf.in b/test/data/valid-config-files/finite-timeout.conf.in new file mode 100644 index 00000000..7d26d715 --- /dev/null +++ b/test/data/valid-config-files/finite-timeout.conf.in @@ -0,0 +1,19 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <!-- Our well-known bus type, don't change this --> + <type>session</type> + <listen>@TEST_LISTEN@</listen> + + <policy context="default"> + <!-- Allow everything to be sent --> + <allow send_destination="*" eavesdrop="true"/> + <!-- Allow everything to be received --> + <allow eavesdrop="true"/> + <!-- Allow anyone to own anything --> + <allow own="*"/> + </policy> + + <!-- Forcibly time out method calls after 100ms --> + <limit name="reply_timeout">100</limit> +</busconfig> diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c index dc0f1317..1cd51d20 100644 --- a/test/dbus-daemon.c +++ b/test/dbus-daemon.c @@ -43,6 +43,31 @@ #include "test-utils.h" +/* Platforms where we know that credentials-passing passes both the + * uid and the pid. Please keep these in alphabetical order. + * + * These platforms should #error in _dbus_read_credentials_socket() + * if we didn't detect their flavour of credentials-passing, since that + * would be a regression. + */ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__linux__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) +# define UNIX_USER_SHOULD_WORK +# define PID_SHOULD_WORK +#endif + +/* Platforms where we know that credentials-passing passes the + * uid, but not necessarily the pid. Again, alphabetical order please. + * + * These platforms should also #error in _dbus_read_credentials_socket() + * if we didn't detect their flavour of credentials-passing. + */ +#if 0 /* defined(__your_platform_here__) */ +# define UNIX_USER_SHOULD_WORK +#endif + typedef struct { gboolean skip; @@ -57,6 +82,7 @@ typedef struct { DBusConnection *right_conn; gboolean right_conn_echo; + gboolean wait_forever_called; } Fixture; #define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__) @@ -157,11 +183,19 @@ echo_filter (DBusConnection *connection, DBusMessage *message, void *user_data) { + Fixture *f = user_data; DBusMessage *reply; if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + /* WaitForever() never replies, emulating a service that has got stuck */ + if (dbus_message_is_method_call (message, "com.example", "WaitForever")) + { + f->wait_forever_called = TRUE; + return DBUS_HANDLER_RESULT_HANDLED; + } + reply = dbus_message_new_method_return (message); if (reply == NULL) @@ -257,7 +291,7 @@ setup (Fixture *f, static void add_echo_filter (Fixture *f) { - if (!dbus_connection_add_filter (f->right_conn, echo_filter, NULL, NULL)) + if (!dbus_connection_add_filter (f->right_conn, echo_filter, f, NULL)) g_error ("OOM"); f->right_conn_echo = TRUE; @@ -343,6 +377,80 @@ pending_call_store_reply (DBusPendingCall *pc, } static void +test_no_reply (Fixture *f, + gconstpointer context) +{ + const Config *config = context; + DBusMessage *m; + DBusPendingCall *pc; + DBusMessage *reply = NULL; + enum { TIMEOUT, DISCONNECT } mode; + gboolean ok; + + if (f->skip) + return; + + g_test_bug ("76112"); + + if (config != NULL && config->config_file != NULL) + mode = TIMEOUT; + else + mode = DISCONNECT; + + m = dbus_message_new_method_call ( + dbus_bus_get_unique_name (f->right_conn), "/", + "com.example", "WaitForever"); + + add_echo_filter (f); + + if (m == NULL) + g_error ("OOM"); + + if (!dbus_connection_send_with_reply (f->left_conn, m, &pc, + DBUS_TIMEOUT_INFINITE) || + pc == NULL) + g_error ("OOM"); + + if (dbus_pending_call_get_completed (pc)) + pending_call_store_reply (pc, &reply); + else if (!dbus_pending_call_set_notify (pc, pending_call_store_reply, &reply, + NULL)) + g_error ("OOM"); + + dbus_pending_call_unref (pc); + dbus_message_unref (m); + + if (mode == DISCONNECT) + { + while (!f->wait_forever_called) + test_main_context_iterate (f->ctx, TRUE); + + dbus_connection_remove_filter (f->right_conn, echo_filter, f); + dbus_connection_close (f->right_conn); + dbus_connection_unref (f->right_conn); + f->right_conn = NULL; + } + + while (reply == NULL) + test_main_context_iterate (f->ctx, TRUE); + + /* using inefficient string comparison for better assertion message */ + g_assert_cmpstr ( + dbus_message_type_to_string (dbus_message_get_type (reply)), ==, + dbus_message_type_to_string (DBUS_MESSAGE_TYPE_ERROR)); + ok = dbus_set_error_from_message (&f->e, reply); + g_assert (ok); + g_assert_cmpstr (f->e.name, ==, DBUS_ERROR_NO_REPLY); + + if (mode == DISCONNECT) + g_assert_cmpstr (f->e.message, ==, + "Message recipient disconnected from message bus without replying"); + else + g_assert_cmpstr (f->e.message, ==, + "Message did not receive a reply (timeout by message bus)"); +} + +static void test_creds (Fixture *f, gconstpointer context) { @@ -445,8 +553,11 @@ test_creds (Fixture *f, dbus_message_iter_next (&arr_iter); } -#ifdef G_OS_UNIX +#ifdef UNIX_USER_SHOULD_WORK g_assert (seen & SEEN_UNIX_USER); +#endif + +#ifdef PID_SHOULD_WORK g_assert (seen & SEEN_PID); #endif @@ -458,6 +569,72 @@ test_creds (Fixture *f, } static void +test_processid (Fixture *f, + gconstpointer context) +{ + const char *unique = dbus_bus_get_unique_name (f->left_conn); + DBusMessage *m = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetConnectionUnixProcessID"); + DBusPendingCall *pc; + DBusError error = DBUS_ERROR_INIT; + guint32 pid; + + if (m == NULL) + g_error ("OOM"); + + if (!dbus_message_append_args (m, + DBUS_TYPE_STRING, &unique, + DBUS_TYPE_INVALID)) + g_error ("OOM"); + + if (!dbus_connection_send_with_reply (f->left_conn, m, &pc, + DBUS_TIMEOUT_USE_DEFAULT) || + pc == NULL) + g_error ("OOM"); + + dbus_message_unref (m); + m = NULL; + + if (dbus_pending_call_get_completed (pc)) + pending_call_store_reply (pc, &m); + else if (!dbus_pending_call_set_notify (pc, pending_call_store_reply, + &m, NULL)) + g_error ("OOM"); + + while (m == NULL) + test_main_context_iterate (f->ctx, TRUE); + + if (dbus_message_get_args (m, &error, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID)) + { + g_assert_cmpstr (dbus_message_get_signature (m), ==, "u"); + assert_no_error (&error); + + g_message ("GetConnectionUnixProcessID returned %u", pid); + +#ifdef G_OS_UNIX + g_assert_cmpuint (pid, ==, getpid ()); +#elif defined(G_OS_WIN32) + g_assert_cmpuint (pid, ==, GetCurrentProcessId ()); +#else + g_assert_not_reached (); +#endif + } + else + { + g_assert_cmpstr (error.name, ==, DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN); + +#ifdef PID_SHOULD_WORK + g_error ("Expected pid to be passed, but got %s: %s", + error.name, error.message); +#endif + + dbus_error_free (&error); + } +} + +static void test_canonical_path_uae (Fixture *f, gconstpointer context) { @@ -560,7 +737,7 @@ teardown (Fixture *f, { if (f->right_conn_echo) { - dbus_connection_remove_filter (f->right_conn, echo_filter, NULL); + dbus_connection_remove_filter (f->right_conn, echo_filter, f); f->right_conn_echo = FALSE; } @@ -588,6 +765,10 @@ static Config limited_config = { "34393", 10000, "valid-config-files/incoming-limit.conf" }; +static Config finite_timeout_config = { + NULL, 1, "valid-config-files/finite-timeout.conf" +}; + int main (int argc, char **argv) @@ -598,7 +779,12 @@ main (int argc, g_test_add ("/echo/session", Fixture, NULL, setup, test_echo, teardown); g_test_add ("/echo/limited", Fixture, &limited_config, setup, test_echo, teardown); + g_test_add ("/no-reply/disconnect", Fixture, NULL, + setup, test_no_reply, teardown); + g_test_add ("/no-reply/timeout", Fixture, &finite_timeout_config, + setup, test_no_reply, teardown); g_test_add ("/creds", Fixture, NULL, setup, test_creds, teardown); + g_test_add ("/processid", Fixture, NULL, setup, test_processid, teardown); g_test_add ("/canonical-path/uae", Fixture, NULL, setup, test_canonical_path_uae, teardown); diff --git a/test/fdpass.c b/test/fdpass.c new file mode 100644 index 00000000..d41dac5f --- /dev/null +++ b/test/fdpass.c @@ -0,0 +1,853 @@ +/* + * Copyright © 2010-2012 Nokia Corporation + * Copyright © 2014 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 <dbus/dbus.h> +#include <dbus/dbus-internals.h> +#include <dbus/dbus-sysdeps.h> + +#include <glib.h> + +#include <string.h> + +#ifdef G_OS_UNIX +# include <dbus/dbus-sysdeps-unix.h> + +# include <errno.h> +# include <fcntl.h> +# ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +# endif +# include <sys/stat.h> +# include <sys/time.h> +# include <sys/types.h> +# include <unistd.h> +#endif + +#include "test-utils.h" + +/* Arbitrary; included here to avoid relying on the default */ +#define MAX_MESSAGE_UNIX_FDS 20 +/* This test won't work on Linux unless this is true. */ +_DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS <= 253); + +/* Arbitrary; included here to avoid relying on the default. */ +#define MAX_INCOMING_UNIX_FDS (MAX_MESSAGE_UNIX_FDS * 4) + +/* Arbitrary, except that MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES must be + * less than the process's file descriptor limit. */ +#define SOME_MESSAGES 50 + +/* Linux won't allow more than 253 fds per sendmsg(). */ +#define TOO_MANY_FDS 255 +_DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS < TOO_MANY_FDS); + +/* As in test/relay.c, this is a miniature dbus-daemon: we relay messages + * from the client on the left to the client on the right. + * + * left socket left dispatch right socket right + * client ===========> server --------------> server ===========> client + * conn conn conn conn + */ + +typedef struct { + TestMainContext *ctx; + DBusError e; + + DBusServer *server; + + DBusConnection *left_client_conn; + DBusConnection *left_server_conn; + + DBusConnection *right_server_conn; + DBusConnection *right_client_conn; + /* queue of DBusMessage received by right_client_conn */ + GQueue messages; + + int fd_before; +} Fixture; + +#if !GLIB_CHECK_VERSION (2, 38, 0) +#define g_test_skip(s) my_test_skip (s) +static inline void my_test_skip (const gchar *s) +{ + g_message ("SKIP: %s", s); +} +#endif + +#ifdef HAVE_UNIX_FD_PASSING + +static void oom (const gchar *doing) G_GNUC_NORETURN; + +static void +oom (const gchar *doing) +{ + g_error ("out of memory (%s)", doing); +} + +static void +assert_no_error (const DBusError *e) +{ + if (G_UNLIKELY (dbus_error_is_set (e))) + g_error ("expected success but got error: %s: %s", e->name, e->message); +} + +static DBusHandlerResult +left_server_message_cb (DBusConnection *server_conn, + DBusMessage *message, + void *data) +{ + Fixture *f = data; + + g_assert (server_conn == f->left_server_conn); + g_assert (f->right_server_conn != NULL); + + dbus_connection_send (f->right_server_conn, message, NULL); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +right_client_message_cb (DBusConnection *client_conn, + DBusMessage *message, + void *data) +{ + Fixture *f = data; + + g_assert (client_conn == f->right_client_conn); + g_queue_push_tail (&f->messages, dbus_message_ref (message)); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void +new_conn_cb (DBusServer *server, + DBusConnection *server_conn, + void *data) +{ + Fixture *f = data; + + dbus_connection_set_max_message_unix_fds (server_conn, + MAX_MESSAGE_UNIX_FDS); + dbus_connection_set_max_received_unix_fds (server_conn, + MAX_INCOMING_UNIX_FDS); + + if (f->left_server_conn == NULL) + { + f->left_server_conn = dbus_connection_ref (server_conn); + + if (!dbus_connection_add_filter (server_conn, + left_server_message_cb, f, NULL)) + oom ("adding filter"); + } + else + { + g_assert (f->right_server_conn == NULL); + f->right_server_conn = dbus_connection_ref (server_conn); + } + + test_connection_setup (f->ctx, server_conn); +} + +static void +test_connect (Fixture *f, + gconstpointer data G_GNUC_UNUSED) +{ + char *address; + + g_assert (f->left_server_conn == NULL); + g_assert (f->right_server_conn == NULL); + + address = dbus_server_get_address (f->server); + g_assert (address != NULL); + + f->left_client_conn = dbus_connection_open_private (address, &f->e); + assert_no_error (&f->e); + g_assert (f->left_client_conn != NULL); + test_connection_setup (f->ctx, f->left_client_conn); + + /* The left client connection is allowed to behave abusively. */ + dbus_connection_set_max_message_unix_fds (f->left_client_conn, 1000); + dbus_connection_set_max_received_unix_fds (f->left_client_conn, 1000000); + + while (f->left_server_conn == NULL) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + f->right_client_conn = dbus_connection_open_private (address, &f->e); + assert_no_error (&f->e); + g_assert (f->right_client_conn != NULL); + test_connection_setup (f->ctx, f->right_client_conn); + + dbus_free (address); + + while (f->right_server_conn == NULL) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + if (!dbus_connection_add_filter (f->right_client_conn, + right_client_message_cb, f, NULL)) + oom ("adding filter"); + + /* The right client connection is allowed to queue all the messages. */ + dbus_connection_set_max_message_unix_fds (f->right_client_conn, 1000); + dbus_connection_set_max_received_unix_fds (f->right_client_conn, 1000000); + + while (!dbus_connection_get_is_authenticated (f->left_client_conn) || + !dbus_connection_get_is_authenticated (f->right_client_conn) || + !dbus_connection_get_is_authenticated (f->left_server_conn) || + !dbus_connection_get_is_authenticated (f->right_server_conn)) + { + g_printerr ("*"); + test_main_context_iterate (f->ctx, TRUE); + } + + if (!dbus_connection_can_send_type (f->left_client_conn, + DBUS_TYPE_UNIX_FD)) + g_error ("left client connection cannot send Unix fds"); + + if (!dbus_connection_can_send_type (f->left_server_conn, + DBUS_TYPE_UNIX_FD)) + g_error ("left server connection cannot send Unix fds"); + + if (!dbus_connection_can_send_type (f->right_client_conn, + DBUS_TYPE_UNIX_FD)) + g_error ("right client connection cannot send Unix fds"); + + if (!dbus_connection_can_send_type (f->right_server_conn, + DBUS_TYPE_UNIX_FD)) + g_error ("right server connection cannot send Unix fds"); +} +#endif + +static void +setup (Fixture *f, + gconstpointer data G_GNUC_UNUSED) +{ +#ifdef HAVE_UNIX_FD_PASSING + /* We assume that anything with fd-passing supports the unix: transport */ + + f->ctx = test_main_context_get (); + dbus_error_init (&f->e); + g_queue_init (&f->messages); + + f->server = dbus_server_listen ("unix:tmpdir=/tmp", &f->e); + assert_no_error (&f->e); + g_assert (f->server != NULL); + + dbus_server_set_new_connection_function (f->server, + new_conn_cb, f, NULL); + test_server_setup (f->ctx, f->server); + + f->fd_before = open ("/dev/null", O_RDONLY); + + /* this should succeed on any reasonable Unix */ + if (f->fd_before < 0) + g_error ("cannot open /dev/null for reading: %s", g_strerror (errno)); + + _dbus_fd_set_close_on_exec (f->fd_before); +#endif +} + +static void +test_relay (Fixture *f, + gconstpointer data) +{ +#ifdef HAVE_UNIX_FD_PASSING + /* We assume that any platform with working fd-passing is POSIX, + * and therefore has open() and fstat() */ + dbus_uint32_t serial; + DBusMessage *outgoing, *incoming; + int fd_after; + struct stat stat_before; + struct stat stat_after; + + test_connect (f, data); + + outgoing = dbus_message_new_signal ("/com/example/Hello", + "com.example.Hello", "Greeting"); + g_assert (outgoing != NULL); + + if (!dbus_message_append_args (outgoing, + DBUS_TYPE_UNIX_FD, &f->fd_before, + DBUS_TYPE_INVALID)) + oom ("appending fd"); + + if (!dbus_connection_send (f->left_client_conn, outgoing, &serial)) + oom ("sending message"); + + dbus_message_unref (outgoing); + + while (g_queue_get_length (&f->messages) < 1) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1); + + incoming = g_queue_pop_head (&f->messages); + + g_assert (dbus_message_contains_unix_fds (incoming)); + g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_interface (incoming), ==, + "com.example.Hello"); + g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting"); + g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_signature (incoming), ==, + DBUS_TYPE_UNIX_FD_AS_STRING); + g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello"); + g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial); + + if (!dbus_message_get_args (incoming, + &f->e, + DBUS_TYPE_UNIX_FD, &fd_after, + DBUS_TYPE_INVALID)) + g_error ("%s: %s", f->e.name, f->e.message); + + assert_no_error (&f->e); + + if (fstat (f->fd_before, &stat_before) < 0) + g_error ("%s", g_strerror (errno)); + + if (fstat (fd_after, &stat_after) < 0) + g_error ("%s", g_strerror (errno)); + + /* this seems like enough to say "it's the same file" */ + g_assert_cmpint (stat_before.st_dev, ==, stat_after.st_dev); + g_assert_cmpint (stat_before.st_ino, ==, stat_after.st_ino); + g_assert_cmpint (stat_before.st_rdev, ==, stat_after.st_rdev); + + dbus_message_unref (incoming); + + if (close (fd_after) < 0) + g_error ("%s", g_strerror (errno)); + + g_assert (dbus_connection_get_is_connected (f->right_client_conn)); + g_assert (dbus_connection_get_is_connected (f->right_server_conn)); + g_assert (dbus_connection_get_is_connected (f->left_client_conn)); + g_assert (dbus_connection_get_is_connected (f->left_server_conn)); +#else + g_test_skip ("fd-passing not supported on this platform"); +#endif +} + +static void +test_limit (Fixture *f, + gconstpointer data) +{ +#ifdef HAVE_UNIX_FD_PASSING + dbus_uint32_t serial; + DBusMessage *outgoing, *incoming; + int i; + + test_connect (f, data); + + outgoing = dbus_message_new_signal ("/com/example/Hello", + "com.example.Hello", "Greeting"); + g_assert (outgoing != NULL); + + for (i = 0; i < MAX_MESSAGE_UNIX_FDS; i++) + { + if (!dbus_message_append_args (outgoing, + DBUS_TYPE_UNIX_FD, &f->fd_before, + DBUS_TYPE_INVALID)) + oom ("appending fd"); + } + + if (!dbus_connection_send (f->left_client_conn, outgoing, &serial)) + oom ("sending message"); + + dbus_message_unref (outgoing); + + while (g_queue_get_length (&f->messages) < 1) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1); + + incoming = g_queue_pop_head (&f->messages); + + g_assert (dbus_message_contains_unix_fds (incoming)); + g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_interface (incoming), ==, + "com.example.Hello"); + g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting"); + g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello"); + g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial); + + dbus_message_unref (incoming); + + g_assert (dbus_connection_get_is_connected (f->right_client_conn)); + g_assert (dbus_connection_get_is_connected (f->right_server_conn)); + g_assert (dbus_connection_get_is_connected (f->left_client_conn)); + g_assert (dbus_connection_get_is_connected (f->left_server_conn)); +#else + g_test_skip ("fd-passing not supported on this platform"); +#endif +} + +static void +test_too_many (Fixture *f, + gconstpointer data) +{ +#ifdef HAVE_UNIX_FD_PASSING + DBusMessage *outgoing; + int i; + + test_connect (f, data); + + outgoing = dbus_message_new_signal ("/com/example/Hello", + "com.example.Hello", "Greeting"); + g_assert (outgoing != NULL); + + for (i = 0; i < MAX_MESSAGE_UNIX_FDS + GPOINTER_TO_UINT (data); i++) + { + if (!dbus_message_append_args (outgoing, + DBUS_TYPE_UNIX_FD, &f->fd_before, + DBUS_TYPE_INVALID)) + oom ("appending fd"); + } + + if (!dbus_connection_send (f->left_client_conn, outgoing, NULL)) + oom ("sending message"); + + dbus_message_unref (outgoing); + + /* The sender is unceremoniously disconnected. */ + while (dbus_connection_get_is_connected (f->left_client_conn) || + dbus_connection_get_is_connected (f->left_server_conn)) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + /* The message didn't get through without its fds. */ + g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0); + + /* The intended victim is unaffected by the left connection's + * misbehaviour. */ + g_assert (dbus_connection_get_is_connected (f->right_client_conn)); + g_assert (dbus_connection_get_is_connected (f->right_server_conn)); +#else + g_test_skip ("fd-passing not supported on this platform"); +#endif +} + +static void +test_too_many_split (Fixture *f, + gconstpointer data) +{ +#ifdef HAVE_UNIX_FD_PASSING + DBusMessage *outgoing; + int i; + int left_client_socket; + char *payload; + int payload_len; + DBusString buffer; + int fds[TOO_MANY_FDS]; + int done; + + /* This test deliberately pushes up against OS limits, so skip it + * if we don't have enough fds. 4 times the maximum per message + * ought to be enough: that will cover the message, the dup'd fds + * we actually send, the copy that we potentially receive, and some + * spare capacity for everything else. */ +#ifdef HAVE_GETRLIMIT + struct rlimit lim; + + if (getrlimit (RLIMIT_NOFILE, &lim) == 0) + { + if (lim.rlim_cur != RLIM_INFINITY && + lim.rlim_cur < 4 * TOO_MANY_FDS) + { + g_test_skip ("not enough RLIMIT_NOFILE"); + return; + } + } +#endif + + test_connect (f, data); + + outgoing = dbus_message_new_signal ("/com/example/Hello", + "com.example.Hello", "Greeting"); + g_assert (outgoing != NULL); + + /* TOO_MANY_FDS fds are far too many: in particular, Linux doesn't allow + * sending this many in a single sendmsg(). libdbus never splits + * a message between two sendmsg() calls if it can help it, and + * in particular it always sends all the fds with the first sendmsg(), + * but malicious senders might not be so considerate. */ + for (i = 0; i < TOO_MANY_FDS; i++) + { + if (!dbus_message_append_args (outgoing, + DBUS_TYPE_UNIX_FD, &f->fd_before, + DBUS_TYPE_INVALID)) + oom ("appending fd"); + } + + /* This probably shouldn't work for messages with fds, but it does, + * which is convenient for this sort of trickery. */ + if (!dbus_message_marshal (outgoing, &payload, &payload_len)) + oom ("marshalling message"); + + _dbus_string_init_const_len (&buffer, payload, payload_len); + + for (i = 0; i < TOO_MANY_FDS; i++) + { + fds[i] = dup (f->fd_before); + + if (fds[i] < 0) + g_error ("could not dup fd: %s", g_strerror (errno)); + } + + /* This is blatant cheating, and the API documentation specifically + * tells you not use this function in this way. Never do this + * in application code. */ + if (!dbus_connection_get_socket (f->left_client_conn, &left_client_socket)) + g_error ("'unix:' DBusConnection should have had a socket"); + + /* Just to be sure that we're at a message boundary. */ + dbus_connection_flush (f->left_client_conn); + + /* We have too many fds for one sendmsg(), so send the first half + * (rounding down if odd) with the first byte... */ + done = _dbus_write_socket_with_unix_fds (left_client_socket, &buffer, 0, 1, + &fds[0], TOO_MANY_FDS / 2); + + if (done < 0) + g_error ("could not send first byte and first batch of fds: %s", + g_strerror (errno)); + + /* ... and the second half (rounding up if odd) with the rest of + * the message */ + done = _dbus_write_socket_with_unix_fds (left_client_socket, &buffer, 1, + payload_len - 1, &fds[TOO_MANY_FDS / 2], + TOO_MANY_FDS - (TOO_MANY_FDS / 2)); + + if (done < 0) + { + g_error ("could not send rest of message and rest of fds: %s", + g_strerror (errno)); + } + else if (done < payload_len - 1) + { + /* For simplicity, assume the socket buffer is big enough for the + * whole message, which should be < 2 KiB. If this fails on some + * OS, redo this test code to use a proper loop like the real + * libdbus does. */ + g_error ("short write in sendmsg(), fix this test: %d/%d", + done, payload_len - 1); + } + + dbus_free (payload); + + for (i = 0; i < TOO_MANY_FDS; i++) + close (fds[i]); + + dbus_message_unref (outgoing); + + /* The sender is unceremoniously disconnected. */ + while (dbus_connection_get_is_connected (f->left_client_conn) || + dbus_connection_get_is_connected (f->left_server_conn)) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + /* The message didn't get through without its fds. */ + g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0); + + /* The intended victim is unaffected by the left connection's + * misbehaviour. */ + g_assert (dbus_connection_get_is_connected (f->right_client_conn)); + g_assert (dbus_connection_get_is_connected (f->right_server_conn)); +#else + g_test_skip ("fd-passing not supported on this platform"); +#endif +} + +static void +test_flood (Fixture *f, + gconstpointer data) +{ +#ifdef HAVE_UNIX_FD_PASSING + int i, j; + DBusMessage *outgoing[SOME_MESSAGES]; + dbus_uint32_t serial; + + test_connect (f, data); + + for (j = 0; j < SOME_MESSAGES; j++) + { + outgoing[j] = dbus_message_new_signal ("/com/example/Hello", + "com.example.Hello", "Greeting"); + g_assert (outgoing[j] != NULL); + + for (i = 0; i < GPOINTER_TO_UINT (data); i++) + { + if (!dbus_message_append_args (outgoing[j], + DBUS_TYPE_UNIX_FD, &f->fd_before, + DBUS_TYPE_INVALID)) + oom ("appending fd"); + } + } + + /* This is in its own loop so we do it as fast as possible */ + for (j = 0; j < SOME_MESSAGES; j++) + { + if (!dbus_connection_send (f->left_client_conn, outgoing[j], &serial)) + oom ("sending message"); + } + + for (j = 0; j < SOME_MESSAGES; j++) + { + dbus_message_unref (outgoing[j]); + } + + while (g_queue_get_length (&f->messages) < SOME_MESSAGES) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + g_assert_cmpuint (g_queue_get_length (&f->messages), ==, SOME_MESSAGES); + + for (j = 0; j < SOME_MESSAGES; j++) + { + DBusMessage *incoming; + + incoming = g_queue_pop_head (&f->messages); + + g_assert (dbus_message_contains_unix_fds (incoming)); + g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_interface (incoming), ==, + "com.example.Hello"); + g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting"); + g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello"); + + dbus_message_unref (incoming); + } + + g_assert (dbus_connection_get_is_connected (f->right_client_conn)); + g_assert (dbus_connection_get_is_connected (f->right_server_conn)); + g_assert (dbus_connection_get_is_connected (f->left_client_conn)); + g_assert (dbus_connection_get_is_connected (f->left_server_conn)); +#else + g_test_skip ("fd-passing not supported on this platform"); +#endif +} + +static void +test_odd_limit (Fixture *f, + gconstpointer data) +{ +#ifdef HAVE_UNIX_FD_PASSING + DBusMessage *outgoing; + int i; + + test_connect (f, data); + dbus_connection_set_max_message_unix_fds (f->left_server_conn, 7); + dbus_connection_set_max_message_unix_fds (f->right_server_conn, 7); + + outgoing = dbus_message_new_signal ("/com/example/Hello", + "com.example.Hello", "Greeting"); + g_assert (outgoing != NULL); + + for (i = 0; i < 7 + GPOINTER_TO_INT (data); i++) + { + if (!dbus_message_append_args (outgoing, + DBUS_TYPE_UNIX_FD, &f->fd_before, + DBUS_TYPE_INVALID)) + oom ("appending fd"); + } + + if (!dbus_connection_send (f->left_client_conn, outgoing, NULL)) + oom ("sending message"); + + dbus_message_unref (outgoing); + + if (GPOINTER_TO_INT (data) > 0) + { + /* The sender is unceremoniously disconnected. */ + while (dbus_connection_get_is_connected (f->left_client_conn) || + dbus_connection_get_is_connected (f->left_server_conn)) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + /* The message didn't get through without its fds. */ + g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0); + + /* The intended victim is unaffected by the left connection's + * misbehaviour. */ + g_assert (dbus_connection_get_is_connected (f->right_client_conn)); + g_assert (dbus_connection_get_is_connected (f->right_server_conn)); + } + else + { + DBusMessage *incoming; + + /* We're at or under the limit. The message gets through intact. */ + while (g_queue_get_length (&f->messages) < 1) + { + g_print ("."); + test_main_context_iterate (f->ctx, TRUE); + } + + g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1); + + incoming = g_queue_pop_head (&f->messages); + + g_assert (dbus_message_contains_unix_fds (incoming)); + g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_interface (incoming), ==, + "com.example.Hello"); + g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting"); + g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL); + g_assert_cmpstr (dbus_message_get_path (incoming), ==, + "/com/example/Hello"); + + dbus_message_unref (incoming); + + g_assert (dbus_connection_get_is_connected (f->right_client_conn)); + g_assert (dbus_connection_get_is_connected (f->right_server_conn)); + g_assert (dbus_connection_get_is_connected (f->left_client_conn)); + g_assert (dbus_connection_get_is_connected (f->left_server_conn)); + } +#else + g_test_skip ("fd-passing not supported on this platform"); +#endif +} + +static void +teardown (Fixture *f, + gconstpointer data G_GNUC_UNUSED) +{ +#ifdef HAVE_UNIX_FD_PASSING + if (f->left_client_conn != NULL) + { + dbus_connection_close (f->left_client_conn); + dbus_connection_unref (f->left_client_conn); + f->left_client_conn = NULL; + } + + if (f->right_client_conn != NULL) + { + dbus_connection_close (f->right_client_conn); + dbus_connection_unref (f->right_client_conn); + f->right_client_conn = NULL; + } + + if (f->left_server_conn != NULL) + { + dbus_connection_close (f->left_server_conn); + dbus_connection_unref (f->left_server_conn); + f->left_server_conn = NULL; + } + + if (f->right_server_conn != NULL) + { + dbus_connection_close (f->right_server_conn); + dbus_connection_unref (f->right_server_conn); + f->right_server_conn = NULL; + } + + if (f->server != NULL) + { + dbus_server_disconnect (f->server); + dbus_server_unref (f->server); + f->server = NULL; + } + + test_main_context_unref (f->ctx); + + if (f->fd_before >= 0 && close (f->fd_before) < 0) + g_error ("%s", g_strerror (errno)); +#endif +} + +int +main (int argc, + char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add ("/relay", Fixture, NULL, setup, + test_relay, teardown); + g_test_add ("/limit", Fixture, NULL, setup, + test_limit, teardown); + + g_test_add ("/too-many/plus1", Fixture, GUINT_TO_POINTER (1), setup, + test_too_many, teardown); + g_test_add ("/too-many/plus2", Fixture, GUINT_TO_POINTER (2), setup, + test_too_many, teardown); + g_test_add ("/too-many/plus17", Fixture, GUINT_TO_POINTER (17), setup, + test_too_many, teardown); + + g_test_add ("/too-many/split", Fixture, NULL, setup, + test_too_many_split, teardown); + + g_test_add ("/flood/1", Fixture, GUINT_TO_POINTER (1), + setup, test_flood, teardown); +#if MAX_MESSAGE_UNIX_FDS >= 2 + g_test_add ("/flood/half-limit", Fixture, + GUINT_TO_POINTER (MAX_MESSAGE_UNIX_FDS / 2), + setup, test_flood, teardown); +#endif +#if MAX_MESSAGE_UNIX_FDS >= 4 + g_test_add ("/flood/over-half-limit", Fixture, + GUINT_TO_POINTER (3 * MAX_MESSAGE_UNIX_FDS / 4), + setup, test_flood, teardown); +#endif + g_test_add ("/flood/limit", Fixture, + GUINT_TO_POINTER (MAX_MESSAGE_UNIX_FDS), setup, test_flood, teardown); + + g_test_add ("/odd-limit/minus1", Fixture, GINT_TO_POINTER (-1), setup, + test_odd_limit, teardown); + g_test_add ("/odd-limit/at", Fixture, GINT_TO_POINTER (0), setup, + test_odd_limit, teardown); + g_test_add ("/odd-limit/plus1", Fixture, GINT_TO_POINTER (+1), setup, + test_odd_limit, teardown); + g_test_add ("/odd-limit/plus2", Fixture, GINT_TO_POINTER (+2), setup, + test_odd_limit, teardown); + + return g_test_run (); +} diff --git a/test/internals/syslog.c b/test/internals/syslog.c index 7e0eae79..80a0cebb 100644 --- a/test/internals/syslog.c +++ b/test/internals/syslog.c @@ -68,16 +68,18 @@ test_syslog (Fixture *f, { _dbus_init_system_log (FALSE); _dbus_system_log (DBUS_SYSTEM_LOG_INFO, MESSAGE "%d", 42); + _dbus_system_log (DBUS_SYSTEM_LOG_WARNING, MESSAGE "%d", 45); _dbus_system_log (DBUS_SYSTEM_LOG_SECURITY, MESSAGE "%d", 666); exit (0); } g_test_trap_assert_passed (); - g_test_trap_assert_stderr ("*" MESSAGE "42\n*" MESSAGE "666\n*"); + g_test_trap_assert_stderr ("*" MESSAGE "42\n*" MESSAGE "45\n*" MESSAGE "666\n*"); #endif /* manual test (this is the best we can do on Windows) */ _dbus_init_system_log (FALSE); _dbus_system_log (DBUS_SYSTEM_LOG_INFO, MESSAGE "%d", 42); + _dbus_system_log (DBUS_SYSTEM_LOG_WARNING, MESSAGE "%d", 45); _dbus_system_log (DBUS_SYSTEM_LOG_SECURITY, MESSAGE "%d", 666); } diff --git a/test/manual-dir-iter.c b/test/manual-dir-iter.c new file mode 100644 index 00000000..21ac0e95 --- /dev/null +++ b/test/manual-dir-iter.c @@ -0,0 +1,92 @@ +#include <config.h> +#include "test-utils.h" + +#include "dbus/dbus-macros.h" +#include "dbus/dbus-sysdeps.h" + +static void oom (const char *doing) _DBUS_GNUC_NORETURN; +static void die (const char *message) _DBUS_GNUC_NORETURN; + +void +oom (const char *doing) +{ + fprintf (stderr, "*** manual-dir-iter: OOM while %s\n", doing); + exit (1); +} + +void +die (const char *message) +{ + fprintf (stderr, "*** manual-dir-iter: %s\n", message); + exit (1); +} + +static void +debug (const char *message) +{ + fprintf (stdout, "+++ manual-dir-iter: %s\n", message); +} + +int +main (int argc, + char **argv) +{ + DBusString filename; + DBusString dirname; + DBusError tmp_error; + DBusDirIter *dir; + + if (argc != 2) + die ("syntax: manual-dir-iter <path>"); + + dbus_error_init (&tmp_error); + + if (!_dbus_string_init (&filename)) + oom ("init filename"); + + if (!_dbus_string_init (&dirname)) + oom ("init dirname"); + + _dbus_string_append (&dirname, argv[1]); + dir = _dbus_directory_open (&dirname, &tmp_error); + + if (dir == NULL) + { + fprintf (stderr, "could not open directory: %s: %s\n", + tmp_error.name, tmp_error.message); + exit(1); + } + + while (_dbus_directory_get_next_file (dir, &filename, &tmp_error)) + { + DBusString full_path; + if (!_dbus_string_init (&full_path)) + { + oom ("init full_path"); + } + + if (!_dbus_string_copy (&dirname, 0, &full_path, 0)) + { + oom ("copying full_path to dirname"); + } + + if (!_dbus_concat_dir_and_file (&full_path, &filename)) + { + oom ("concat full_path"); + } + debug (_dbus_string_get_const_data (&filename)); + _dbus_string_free (&full_path); + } + + if (dbus_error_is_set (&tmp_error)) + die (tmp_error.message); + + _dbus_string_free (&filename); + + if (dir) + _dbus_directory_close (dir); + + _dbus_verbose ("*** Test dir name exiting\n"); + + return 0; +} diff --git a/test/name-test/test-threads-init.c b/test/name-test/test-threads-init.c index 580ffe14..a517e2a2 100644 --- a/test/name-test/test-threads-init.c +++ b/test/name-test/test-threads-init.c @@ -149,16 +149,6 @@ main (int argc, char *argv[]) &dispatch_cond1, &io_path_cond1); - /* Since 1.7 it is no longer the case that mutex1 != mutex2, because - * initializing global locks automatically initializes locks - * in general. However, it is true that the mutex is not the dummy - * implementation, which is what we really wanted to check here. */ - _dbus_assert (mutex1 != (DBusMutex *) 0xABCDEF); - _dbus_assert (dispatch_mutex1 != (DBusMutex *) 0xABCDEF); - _dbus_assert (dispatch_cond1 != (DBusCondVar *) 0xABCDEF2); - _dbus_assert (io_path_mutex1 != (DBusMutex *) 0xABCDEF); - _dbus_assert (io_path_cond1 != (DBusCondVar *) 0xABCDEF2); - _run_iteration (conn); _dbus_connection_test_get_locks (conn, &mutex2, &dispatch_mutex2, |