xref: /xnu-12377.81.4/tests/signals/sigchld_return.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
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