From f9cc81bbc96a5f125298aec8099001ff925c5309 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 10 Mar 2011 17:51:14 +0000 Subject: [PATCH 11/11] Add a regression test that can reproduce fd.o #34393 The number of messages is arbitrary; the more messages, the more likely the crash is. 2000 messages seem to cause it reliably, on this laptop. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=34393 --- test/.gitignore | 1 + test/Makefile.am | 12 + test/data/valid-config-files/incoming-limit.conf | 18 ++ test/limit.c | 284 ++++++++++++++++++++++ 4 files changed, 315 insertions(+), 0 deletions(-) create mode 100644 test/data/valid-config-files/incoming-limit.conf create mode 100644 test/limit.c diff --git a/test/.gitignore b/test/.gitignore index b329d58..609b795 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -25,3 +25,4 @@ test-shell-service test-names test-loopback test-relay +test-limit diff --git a/test/Makefile.am b/test/Makefile.am index 0ec176e..f0c3c1e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -73,9 +73,21 @@ spawn_test_LDFLAGS=@R_DYNAMIC_LDFLAG@ EXTRA_DIST= modular_tests = \ + test-limit \ test-loopback \ test-relay +TESTS_ENVIRONMENT = \ + TEST_DBUS_DAEMON=@abs_top_builddir@/bus/dbus-daemon$(EXEEXT) \ + TEST_CONFIG_FILES=@abs_top_srcdir@/test/data/valid-config-files + +test_limit_SOURCES = limit.c +test_limit_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS) +test_limit_LDFLAGS = @R_DYNAMIC_LDFLAG@ +test_limit_LDADD = $(top_builddir)/dbus/libdbus-1.la \ + $(GLIB_LIBS) \ + $(DBUS_GLIB_LIBS) + test_loopback_SOURCES = loopback.c test_loopback_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS) test_loopback_LDFLAGS = @R_DYNAMIC_LDFLAG@ diff --git a/test/data/valid-config-files/incoming-limit.conf b/test/data/valid-config-files/incoming-limit.conf new file mode 100644 index 0000000..abfab3f --- /dev/null +++ b/test/data/valid-config-files/incoming-limit.conf @@ -0,0 +1,18 @@ + + + + session + unix:tmpdir=/tmp + + + + + + + + + + + 1 + diff --git a/test/limit.c b/test/limit.c new file mode 100644 index 0000000..907af21 --- /dev/null +++ b/test/limit.c @@ -0,0 +1,284 @@ +/* Regression test for fd.o #34393 + * + * Author: Simon McVittie + * Copyright © 2010 Nokia Corporation + * + * 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 + +#include + +#include +#include + +#include + +#ifdef DBUS_WIN +# include +#else +# include +# include +#endif + +typedef struct { + DBusError e; + GError *ge; + + DBusConnection *left_conn; + DBusConnection *right_conn; + gint daemon_pid; +} Fixture; + +#define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__) +static void +_assert_no_error (const DBusError *e, + const char *file, + int line) +{ + if (G_UNLIKELY (dbus_error_is_set (e))) + g_error ("%s:%d: expected success but got error: %s: %s", + file, line, e->name, e->message); +} + +static gchar * +spawn_dbus_daemon (gchar *binary, + gchar *configuration, + gint *daemon_pid) +{ + GError *error = NULL; + GString *address; + gint address_fd; + gchar *argv[] = { + binary, + configuration, + "--nofork", + "--print-address=1", /* stdout */ + NULL + }; + + g_spawn_async_with_pipes (NULL, /* working directory */ + argv, + NULL, /* envp */ + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, /* child_setup */ + NULL, /* user data */ + daemon_pid, + NULL, /* child's stdin = /dev/null */ + &address_fd, + NULL, /* child's stderr = our stderr */ + &error); + g_assert_no_error (error); + + address = g_string_new (NULL); + + /* polling until the dbus-daemon writes out its address is a bit stupid, + * but at least it's simple, unlike dbus-launch... in principle we could + * use select() here, but life's too short */ + while (1) + { + gssize bytes; + gchar buf[4096]; + gchar *newline; + + bytes = read (address_fd, buf, sizeof (buf)); + + if (bytes > 0) + g_string_append_len (address, buf, bytes); + + newline = strchr (address->str, '\n'); + + if (newline != NULL) + { + g_string_truncate (address, newline - address->str); + break; + } + + g_usleep (G_USEC_PER_SEC / 10); + } + + return g_string_free (address, FALSE); +} + +static DBusConnection * +connect_to_bus (const gchar *address) +{ + DBusConnection *conn; + DBusError error = DBUS_ERROR_INIT; + dbus_bool_t ok; + + conn = dbus_connection_open_private (address, &error); + assert_no_error (&error); + g_assert (conn != NULL); + + ok = dbus_bus_register (conn, &error); + assert_no_error (&error); + g_assert (ok); + g_assert (dbus_bus_get_unique_name (conn) != NULL); + + dbus_connection_setup_with_g_main (conn, NULL); + return conn; +} + +static DBusHandlerResult +echo_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + DBusMessage *reply; + DBusError error = DBUS_ERROR_INIT; + int *sleep_ms = user_data; + + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + reply = dbus_message_new_method_return (message); + + if (reply == NULL) + g_error ("OOM"); + + if (!dbus_connection_send (connection, reply, NULL)) + g_error ("OOM"); + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void +setup (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + gchar *dbus_daemon; + gchar *config; + gchar *address; + + f->ge = NULL; + dbus_error_init (&f->e); + + g_assert (g_getenv ("TEST_DBUS_DAEMON") != NULL && + g_getenv ("TEST_CONFIG_FILES") != NULL); + + dbus_daemon = g_strdup (g_getenv ("TEST_DBUS_DAEMON")); + config = g_strdup_printf ("--config-file=%s/incoming-limit.conf", + g_getenv ("TEST_CONFIG_FILES")); + + address = spawn_dbus_daemon (dbus_daemon, config, &f->daemon_pid); + + g_free (dbus_daemon); + g_free (config); + + f->left_conn = connect_to_bus (address); + f->right_conn = connect_to_bus (address); + g_free (address); + + if (!dbus_connection_add_filter (f->right_conn, echo_filter, NULL, NULL)) + g_error ("OOM"); +} + +static void +pc_notify (DBusPendingCall *pc, + void *data) +{ + guint *received_p = data; + + (*received_p)++; +} + +static void +test_echo (Fixture *f, + gconstpointer count_p) +{ + guint count = GPOINTER_TO_UINT (count_p); + guint sent; + guint received = 0; + + for (sent = 0; sent < count; sent++) + { + DBusMessage *m = dbus_message_new_method_call ( + dbus_bus_get_unique_name (f->right_conn), "/", + "com.example", "Spam"); + DBusPendingCall *pc; + + if (m == NULL) + g_error ("OOM"); + + if (!dbus_connection_send_with_reply (f->left_conn, m, &pc, 0x7FFFFFFF) + || pc == NULL) + g_error ("OOM"); + + if (dbus_pending_call_get_completed (pc)) + pc_notify (pc, &received); + else if (!dbus_pending_call_set_notify (pc, pc_notify, &received, + NULL)) + g_error ("OOM"); + + dbus_pending_call_unref (pc); + dbus_message_unref (m); + } + + while (received < count) + g_main_context_iteration (NULL, TRUE); +} + +static void +teardown (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + dbus_error_free (&f->e); + g_clear_error (&f->ge); + + if (f->left_conn != NULL) + { + dbus_connection_close (f->left_conn); + dbus_connection_unref (f->left_conn); + f->left_conn = NULL; + } + + if (f->right_conn != NULL) + { + dbus_connection_remove_filter (f->right_conn, echo_filter, NULL); + dbus_connection_close (f->right_conn); + dbus_connection_unref (f->right_conn); + f->right_conn = NULL; + } + +#ifdef DBUS_WIN + TerminateProcess (f->daemon_pid, 1); +#else + kill (f->daemon_pid, SIGTERM); +#endif + + g_spawn_close_pid (f->daemon_pid); +} + +int +main (int argc, + char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add ("/echo/2000", Fixture, GUINT_TO_POINTER (2000), + setup, test_echo, teardown); + + return g_test_run (); +} -- 1.7.4.1