1 #include <signal.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <sys/wait.h>
7 #include <sys/types.h>
8 #include <spawn.h>
9 #include <pthread.h>
10
11 #include <darwintest.h>
12
13 T_GLOBAL_META(
14 T_META_RUN_CONCURRENTLY(true),
15 T_META_IGNORECRASHES("sigchld.*"),
16 T_META_CHECK_LEAKS(false),
17 T_META_TAG_VM_PREFERRED,
18 T_META_RADAR_COMPONENT_NAME("xnu"),
19 T_META_RADAR_COMPONENT_VERSION("signals"));
20
21 static int exitcode = 123;
22
23 static sigset_t set;
24
25 static void
dummy_sigchld_handler(int sig)26 dummy_sigchld_handler(int sig)
27 {
28 }
29
30 static void
prepare_for_sigwait()31 prepare_for_sigwait()
32 {
33 /*
34 * SIGCHLD's default disposition to ignore, which means sigwait won't see it pending.
35 * This requires us to set a dummy signal handler.
36 */
37 struct sigaction act;
38 act.sa_handler = dummy_sigchld_handler;
39 sigemptyset(&act.sa_mask);
40 act.sa_flags = 0;
41 sigaction(SIGCHLD, &act, NULL);
42
43 /* Now block SIGCHLD */
44 sigemptyset(&set);
45 sigaddset(&set, SIGCHLD);
46 sigprocmask(SIG_BLOCK, &set, NULL);
47 }
48
49 static void
wait_for_signal(int expected_exitcode,pid_t expected_pid)50 wait_for_signal(int expected_exitcode, pid_t expected_pid)
51 {
52 siginfo_t siginfo;
53 int sig, result;
54
55 result = sigwait(&set, &sig);
56 T_ASSERT_POSIX_SUCCESS(result, "sigwait failed");
57 T_ASSERT_EQ_INT(sig, SIGCHLD, "sigwait returned SIGCHLD");
58
59 result = waitid(P_PID, expected_pid, &siginfo, WEXITED | WNOHANG);
60 T_ASSERT_POSIX_SUCCESS(result, "waitid failed");
61 T_ASSERT_NE_INT(siginfo.si_pid, 0, "waitid returned no child");
62
63 T_ASSERT_EQ_INT(siginfo.si_signo, SIGCHLD, "si_signo is SIGCHLD");
64 T_ASSERT_EQ_INT(siginfo.si_code, CLD_EXITED, "si_code is CLD_EXITED");
65 T_ASSERT_EQ_INT(siginfo.si_status, expected_exitcode, "si_status");
66 }
67
68
69 T_DECL(sigchldreturn, "checks that a child process exited with an exitcode returns correctly to parent")
70 {
71 int pid;
72
73 prepare_for_sigwait();
74
75 /* Now fork a child that just exits */
76 pid = fork();
77 T_QUIET; T_ASSERT_NE_INT(pid, -1, "fork() failed!");
78
79 if (pid == 0) {
80 /* Child process! */
81 exit(exitcode);
82 }
83
84 wait_for_signal(exitcode, pid);
85 }
86
87 T_DECL(sigabrt_test, "check that child process' exitcode contains signum = SIGABRT")
88 {
89 int ret;
90 siginfo_t siginfo;
91 pid_t pid = fork();
92 int expected_signal = SIGABRT;
93 if (pid == 0) {
94 /* child exits with SIGABRT */
95 T_LOG("In child process. Now signalling SIGABRT");
96 (void)signal(SIGABRT, SIG_DFL);
97 raise(SIGABRT);
98 T_LOG("Child should not print");
99 } else {
100 ret = waitid(P_PID, (id_t) pid, &siginfo, WEXITED);
101 T_ASSERT_POSIX_SUCCESS(0, "waitid");
102 if (siginfo.si_signo != SIGCHLD) {
103 T_FAIL("Signal was not SIGCHLD.");
104 }
105 T_LOG("si_status = 0x%x , expected = 0x%x \n", siginfo.si_status, expected_signal);
106 if (siginfo.si_status != expected_signal) {
107 T_FAIL("Unexpected exitcode");
108 }
109 }
110 }
111
112 T_DECL(sigkill_test, "check that child process' exitcode contains signum = SIGKILL")
113 {
114 int ret;
115 siginfo_t siginfo;
116 pid_t pid = fork();
117 int expected_signal = SIGKILL;
118 if (pid == 0) {
119 /* child exits with SIGKILL */
120 T_LOG("In child process. Now signalling SIGKILL");
121 raise(SIGKILL);
122 T_LOG("Child should not print");
123 } else {
124 ret = waitid(P_PID, (id_t) pid, &siginfo, WEXITED);
125 T_ASSERT_POSIX_SUCCESS(0, "waitid");
126 if (siginfo.si_signo != SIGCHLD) {
127 T_FAIL("Signal was not SIGCHLD.");
128 }
129 T_LOG("si_status = 0x%x , expected = 0x%x \n", siginfo.si_status, expected_signal);
130 if (siginfo.si_status != expected_signal) {
131 T_FAIL("Unexpected exitcode");
132 }
133 }
134 }
135
136 T_DECL(sigchild_posix_spawn_fail, "check SIGCHLD is correctly delivered when posix_spawn fails")
137 {
138 int pid;
139 char *args[4];
140
141 exitcode = 0;
142
143 prepare_for_sigwait();
144
145 args[0] = "sh";
146 args[1] = "-c";
147 args[2] = "exit 0";
148 args[3] = NULL;
149
150 T_ASSERT_POSIX_SUCCESS(posix_spawn(&pid, "/bin/sh", NULL, NULL, args, NULL), "posix_spawn failed");
151
152 for (int i = 0; i < 500; i++) {
153 int ret = posix_spawn(&pid, "does not exist", NULL, NULL, args, NULL);
154 T_QUIET; T_ASSERT_EQ(ret, ENOENT, "posix_spawn should fail with ENOENT");
155 }
156
157 wait_for_signal(exitcode, pid);
158 }
159