diff options
| author | Andreas Schneider <asn@samba.org> | 2017-11-28 15:41:05 +0100 |
|---|---|---|
| committer | Andreas Schneider <asn@samba.org> | 2017-12-04 12:23:47 +0100 |
| commit | 3f804113608f0c2f04a8212fbacf569b67dd0a19 (patch) | |
| tree | b90a59dadb65e40420efd4b8e0d12424c098020d | |
| parent | 3547fdfc094f4588375cb7b1716461f8f328b643 (diff) | |
| download | socket_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>
| -rw-r--r-- | tests/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | tests/test_fork_thread_deadlock.c | 98 | ||||
| -rw-r--r-- | tests/thread_deadlock.c | 70 |
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); +} |
