summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorAndreas Schneider <asn@samba.org>2017-11-28 15:41:05 +0100
committerAndreas Schneider <asn@samba.org>2017-12-04 12:23:47 +0100
commit3f804113608f0c2f04a8212fbacf569b67dd0a19 (patch)
treeb90a59dadb65e40420efd4b8e0d12424c098020d /tests
parent3547fdfc094f4588375cb7b1716461f8f328b643 (diff)
downloadsocket_wrapper-3f804113608f0c2f04a8212fbacf569b67dd0a19.tar.gz
socket_wrapper-3f804113608f0c2f04a8212fbacf569b67dd0a19.tar.xz
socket_wrapper-3f804113608f0c2f04a8212fbacf569b67dd0a19.zip
tests: Add a thread deadlock reproducer
Signed-off-by: Andreas Schneider <asn@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt8
-rw-r--r--tests/test_fork_thread_deadlock.c98
-rw-r--r--tests/thread_deadlock.c70
3 files changed, 175 insertions, 1 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 32a457e..b86cc84 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -37,7 +37,8 @@ set(SWRAP_TESTS
test_echo_udp_sendmsg_recvmsg
test_swrap_unit
test_max_sockets
- test_close_failure)
+ test_close_failure
+ test_fork_thread_deadlock)
if (HAVE_STRUCT_MSGHDR_MSG_CONTROL)
set(SWRAP_TESTS ${SWRAP_TESTS} test_sendmsg_recvmsg_fd)
@@ -60,3 +61,8 @@ foreach(_SWRAP_TEST ${SWRAP_TESTS})
ENVIRONMENT LD_PRELOAD=${SOCKET_WRAPPER_LOCATION})
endif()
endforeach()
+
+# test_fork_pthread
+add_library(thread_deadlock SHARED thread_deadlock.c)
+target_link_libraries(thread_deadlock ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries(test_fork_thread_deadlock thread_deadlock)
diff --git a/tests/test_fork_thread_deadlock.c b/tests/test_fork_thread_deadlock.c
new file mode 100644
index 0000000..6cd1ca0
--- /dev/null
+++ b/tests/test_fork_thread_deadlock.c
@@ -0,0 +1,98 @@
+#include "config.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+
+/*
+ * This reproduces and issue if we get a signal after the pthread_atfork()
+ * prepare function of socket wrapper has been called.
+ *
+ * The order how pthread_atfork() handlers are set up is:
+ * -> application
+ * -> preloaded libraries
+ * -> libraries
+ *
+ * We have a library called thread_deadlock.
+ *
+ * This library registers a thread_deadlock_prepare() function via
+ * pthread_atfork().
+ *
+ * So pthread_atfork() registers the prepare function in the follow order:
+ * -> swrap_thread_prepare()
+ * -> thread_deadlock_prepare()
+ *
+ * In this test we fork and the swrap_thread_prepare() locks the mutex for
+ * symbol binding.
+ * Then thread_deadlock_prepare() is called which sends a signal to the parent
+ * process of this test. The signal triggers the signal handler below.
+ *
+ * When we call write() in the signal handler, we will try to bind the libc symbol
+ * and want to lock the symbol binding mutex. As it is already locked we run into
+ * a deadlock.
+ */
+
+static void test_swrap_signal_handler(int signum)
+{
+ fprintf(stderr, "PID: %d, SIGNUM: %d\n", getpid(), signum);
+ write(1, "DEADLOCK?\n", 10);
+}
+
+static void test_swrap_fork_pthread(void **state)
+{
+ pid_t pid;
+ struct sigaction act = {
+ .sa_handler = test_swrap_signal_handler,
+ .sa_flags = 0,
+ };
+
+ (void)state; /* unused */
+
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGUSR1, &act, NULL);
+
+ pid = fork();
+ assert_return_code(pid, errno);
+
+ /* child */
+ if (pid == 0) {
+ exit(0);
+ }
+
+ /* parent */
+ if (pid > 0) {
+ pid_t child_pid;
+ int wstatus = -1;
+
+ child_pid = waitpid(-1, &wstatus, 0);
+ assert_return_code(child_pid, errno);
+
+ assert_true(WIFEXITED(wstatus));
+
+ assert_int_equal(WEXITSTATUS(wstatus), 0);
+ }
+}
+
+int main(void)
+{
+ int rc;
+
+ const struct CMUnitTest swrap_tests[] = {
+ cmocka_unit_test(test_swrap_fork_pthread),
+ };
+
+ rc = cmocka_run_group_tests(swrap_tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/tests/thread_deadlock.c b/tests/thread_deadlock.c
new file mode 100644
index 0000000..b72367f
--- /dev/null
+++ b/tests/thread_deadlock.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 Andreas Schneider <asn@samba.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <signal.h>
+#include <pthread.h>
+
+#ifdef HAVE_CONSTRUCTOR_ATTRIBUTE
+#define CONSTRUCTOR_ATTRIBUTE __attribute__ ((constructor))
+#else
+#define CONSTRUCTOR_ATTRIBUTE
+#endif /* HAVE_CONSTRUCTOR_ATTRIBUTE */
+
+void thread_deadlock_constructor(void) CONSTRUCTOR_ATTRIBUTE;
+
+static void thread_deadlock_prepare(void)
+{
+ pthread_kill(pthread_self(), SIGUSR1);
+ return;
+}
+
+static void thread_deadlock_parent(void)
+{
+ return;
+}
+
+static void thread_deadlock_child(void)
+{
+ return;
+}
+
+void thread_deadlock_constructor(void)
+{
+ pthread_atfork(&thread_deadlock_prepare,
+ &thread_deadlock_parent,
+ &thread_deadlock_child);
+}