xref: /xnu-10002.41.9/tests/signal_exit_reason.c (revision 699cd48037512bf4380799317ca44ca453c82f57)
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