1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
#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)
{
ssize_t w;
fprintf(stderr, "PID: %u, SIGNUM: %d\n", (unsigned int)getpid(), signum);
w = write(1, "DEADLOCK?\n", 10);
fprintf(stderr, "WRITE: %zu\n", w);
}
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;
}
|