1 /*
2 * Copyright (c) 2023 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * These tests verify that proc_pidinfo returns the expected exit reason and
31 * namespace for signal-related process termination.
32 */
33
34 #include <darwintest.h>
35 #include <signal.h>
36 #include <libproc.h>
37 #include <sys/wait.h>
38 #include <sys/reason.h>
39 #include <stdlib.h>
40 #include <dispatch/dispatch.h>
41 #include <unistd.h>
42
43 T_GLOBAL_META(
44 T_META_NAMESPACE("xnu.misc"),
45 T_META_RADAR_COMPONENT_NAME("xnu"),
46 T_META_RADAR_COMPONENT_VERSION("misc"));
47
48 static dispatch_queue_t exit_queue;
49
50 static void
cleanup(void)51 cleanup(void)
52 {
53 dispatch_release(exit_queue);
54 }
55
56 static void
57 dispatch_test(void (^test)(void))
58 {
59 // Use dispatch to schedule DISPATCH_PROC_EXIT blocks to read out exit reasons
60 exit_queue = dispatch_queue_create("exit queue", DISPATCH_QUEUE_SERIAL);
61 dispatch_async(dispatch_get_main_queue(), ^{
62 test();
63 });
64 T_ATEND(cleanup);
65 dispatch_main();
66 }
67
68 static void
check_exit_reason(int pid,uint64_t expected_reason_namespace,uint64_t expected_signal)69 check_exit_reason(int pid, uint64_t expected_reason_namespace, uint64_t expected_signal)
70 {
71 T_LOG("check_exit_reason %d", expected_signal);
72 int ret, status;
73 struct proc_exitreasonbasicinfo exit_reason;
74
75 T_QUIET; T_ASSERT_POSIX_SUCCESS(
76 ret = proc_pidinfo(pid, PROC_PIDEXITREASONBASICINFO, 1, &exit_reason, PROC_PIDEXITREASONBASICINFOSIZE),
77 "verify proc_pidinfo success"
78 );
79 T_QUIET; T_ASSERT_EQ(ret, PROC_PIDEXITREASONBASICINFOSIZE, "retrieve basic exit reason info");
80
81 waitpid(pid, &status, 0);
82 T_QUIET; T_EXPECT_FALSE(WIFEXITED(status), "process did not exit normally");
83 T_QUIET; T_EXPECT_TRUE(WIFSIGNALED(status), "process was terminated because of a signal");
84 T_QUIET; T_EXPECT_EQ(WTERMSIG(status), expected_signal, "process should terminate due to signal %llu", expected_signal);
85
86 T_EXPECT_EQ(exit_reason.beri_namespace, expected_reason_namespace, "expect OS_REASON_SIGNAL");
87 T_EXPECT_EQ(exit_reason.beri_code, expected_signal, "expect reason code: %llu", expected_signal);
88 }
89
90 static void
wait_collect_exit_reason(int pid,int signal)91 wait_collect_exit_reason(int pid, int signal)
92 {
93 dispatch_source_t ds_proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, exit_queue);
94 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
95 dispatch_source_set_event_handler(ds_proc, ^{
96 check_exit_reason(pid, OS_REASON_SIGNAL, signal);
97 dispatch_semaphore_signal(sem);
98 });
99 dispatch_activate(ds_proc);
100
101 // Wait till exit reason is processed
102 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
103 dispatch_release(ds_proc);
104 dispatch_release(sem);
105 }
106
107 static void
__test_exit_reason_abort()108 __test_exit_reason_abort()
109 {
110 pid_t child = fork();
111 if (child > 0) {
112 wait_collect_exit_reason(child, SIGABRT);
113 } else {
114 abort();
115 }
116 }
117
118 T_DECL(test_exit_reason_abort, "tests exit reason for abort()")
119 {
120 dispatch_test(^{
121 __test_exit_reason_abort();
122 T_END;
123 });
124 }
125
126 static void
__test_exit_reason_external_signal(int signal)127 __test_exit_reason_external_signal(int signal)
128 {
129 T_LOG("Testing external signal %d", signal);
130 pid_t child = fork();
131 if (child > 0) {
132 // Send external signal
133 kill(child, signal);
134 wait_collect_exit_reason(child, signal);
135 } else {
136 pause();
137 }
138 }
139
140 T_DECL(test_exit_reason_external_signal, "tests exit reason for external signals")
141 {
142 dispatch_test(^{
143 __test_exit_reason_external_signal(SIGABRT);
144 __test_exit_reason_external_signal(SIGKILL);
145 __test_exit_reason_external_signal(SIGSYS);
146 __test_exit_reason_external_signal(SIGUSR1);
147 T_END;
148 });
149 }
150
151 struct pthread_kill_helper_args {
152 pthread_t *pthread;
153 int signal;
154 };
155
156 static void
pthread_kill_helper(void * msg)157 pthread_kill_helper(void *msg)
158 {
159 struct pthread_kill_helper_args *args = (struct pthread_kill_helper_args *)msg;
160 pthread_kill(*args->pthread, args->signal);
161 }
162
163 static void
__test_exit_reason_pthread_kill_self(int signal)164 __test_exit_reason_pthread_kill_self(int signal)
165 {
166 T_LOG("Testing pthread_kill for signal %d", signal);
167 pid_t child = fork();
168 if (child > 0) {
169 wait_collect_exit_reason(child, signal);
170 } else {
171 pthread_t t;
172 struct pthread_kill_helper_args args = {&t, signal};
173 pthread_create(&t, NULL, pthread_kill_helper, (void *)&args);
174 pthread_join(t, NULL);
175 }
176 }
177
178 T_DECL(test_exit_reason_pthread_kill_self, "tests exit reason for pthread_kill on caller thread")
179 {
180 dispatch_test(^{
181 __test_exit_reason_pthread_kill_self(SIGABRT);
182 __test_exit_reason_pthread_kill_self(SIGKILL);
183 __test_exit_reason_pthread_kill_self(SIGSYS);
184 __test_exit_reason_pthread_kill_self(SIGUSR1);
185 T_END;
186 });
187 }
188