1 /*
2 * Check tracing of looping threads.
3 *
4 * Copyright (c) 2009-2019 The strace developers.
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "tests.h"
11 #include <assert.h>
12 #include <errno.h>
13 #include <pthread.h>
14 #include <signal.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <sys/wait.h>
19
20 static void *
21 thread(void *arg)
22 {
23 for (;;)
24 getuid();
25 return arg;
26 }
27
28 int
29 main(int ac, const char *av[])
30 {
31 assert(ac == 3);
32
33 int timeout = atoi(av[1]);
34 assert(timeout > 0);
35
36 int num_threads = atoi(av[2]);
37 assert(num_threads > 0);
38
39 /*
40 * Unblock all signals.
41 */
42 static sigset_t mask;
43 if (sigprocmask(SIG_SETMASK, &mask, NULL))
44 perror_msg_and_fail("sigprocmask");
45
46 /*
47 * Reset SIGALRM and SIGHUP signal handlers.
48 */
49 static const struct sigaction sa_def = { .sa_handler = SIG_DFL };
50 if (sigaction(SIGHUP, &sa_def, NULL))
51 perror_msg_and_fail("sigaction SIGHUP");
52 if (sigaction(SIGALRM, &sa_def, NULL))
53 perror_msg_and_fail("sigaction SIGALRM");
54
55 /*
56 * Create a new process group.
57 */
58 if (setpgid(0, 0))
59 perror_msg_and_fail("setpgid");
60
61 /*
62 * Set an alarm clock.
63 */
64 alarm(timeout);
65
66 /*
67 * When the main process terminates, the process group becomes orphaned.
68 * If any member of the orphaned process group is stopped, then
69 * a SIGHUP signal followed by a SIGCONT signal is sent to each process
70 * in the orphaned process group.
71 * Create a process in a stopped state to activate this behaviour.
72 */
73 const pid_t stopped = fork();
74 if (stopped < 0)
75 perror_msg_and_fail("fork");
76 if (!stopped) {
77 raise(SIGSTOP);
78 _exit(0);
79 }
80
81 /*
82 * Wait for the process to stop.
83 */
84 int status;
85 if (waitpid(stopped, &status, WUNTRACED) != stopped)
86 perror_msg_and_fail("waitpid WUNTRACED");
87 if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
88 error_msg_and_fail("waitpid WUNTRACED: "
89 "unexpected wait status %d", status);
90 /*
91 * Create all threads in a subprocess, this guarantees that
92 * their tracer will not be their parent.
93 */
94 pid_t pid = fork();
95 if (pid < 0)
96 perror_msg_and_fail("fork");
97 if (!pid) {
98 for (int i = 0; i < num_threads; i++) {
99 pthread_t t;
100 if ((errno = pthread_create(&t, NULL, thread, NULL))) {
101 if (EAGAIN == errno)
102 break;
103 perror_msg_and_fail("pthread_create #%d", i);
104 }
105 }
106
107 /* This terminates all threads created above. */
108 _exit(0);
109 }
110
111 if (waitpid(pid, &status, 0) != pid)
112 perror_msg_and_fail("waitpid");
113 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
114 error_msg_and_fail("waitpid: unexpected wait status %d",
115 status);
116
117 /*
118 * Make the process group orphaned.
119 */
120 return 0;
121 }