aboutsummaryrefslogtreecommitdiff
path: root/tests/test_fork_thread_deadlock.c
blob: 7d4d4a765b30015710dad0093b19af093477d3ba (plain)
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
#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: %u, SIGNUM: %d\n", (unsigned int)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;
}