1 #include <stdio.h>
2 #include <sys/sysctl.h>
3 #include <spawn_private.h>
4 #include <signal.h>
5 #include <sys/reason.h>
6
7 #ifdef T_NAMESPACE
8 #undef T_NAMESPACE
9 #endif
10 #include <darwintest.h>
11 #include <darwintest_utils.h>
12
13 T_GLOBAL_META(
14 T_META_RADAR_COMPONENT_NAME("xnu"),
15 T_META_RADAR_COMPONENT_VERSION("spawn"),
16 T_META_NAMESPACE("xnu.spawn"));
17
18 extern char **environ;
19
20 #define SYSCTL_CRASH_BEHAVIOR_TEST_MODE "kern.crash_behavior_test_mode=1"
21 #define SYSCTL_CRASH_BEHAVIOR_WOULD_PANIC "kern.crash_behavior_test_would_panic"
22
23 #define TEST_REASON_CODE 5
24
25 static void
_do_set_crash_behavior_test(char * child_mode,int signal,uint32_t flags,bool expect_panic)26 _do_set_crash_behavior_test(char *child_mode, int signal, uint32_t flags, bool expect_panic)
27 {
28 bool should_wait = (strcmp(child_mode, "wait") == 0);
29 bool reason = (strcmp(child_mode, "reason") == 0);
30 bool reason_signal = (strcmp(child_mode, "reason_signal") == 0);
31 bool dirty = (strcmp(child_mode, "dirty") == 0);
32 bool shutdown = (strcmp(child_mode, "clean") == 0) || dirty;
33 uint64_t deadline = mach_continuous_time();
34
35 // 0. clear SYSCTL_CRASH_BEHAVIOR_WOULD_PANIC
36 int would_panic = 0;
37 size_t length = sizeof(would_panic);
38 int ret = sysctlbyname(SYSCTL_CRASH_BEHAVIOR_WOULD_PANIC, NULL, 0, &would_panic, length);
39 T_ASSERT_POSIX_SUCCESS(ret, "Clearing SYSCTL_CRASH_BEHAVIOR_WOULD_PANIC");
40
41 // 1. posix_spawn a child process
42 char *test_program = "./posix_spawnattr_set_crash_behavior_np_child";
43 char *child_args[3];
44
45 posix_spawnattr_t attrs;
46 posix_spawnattr_init(&attrs);
47
48 ret = posix_spawnattr_set_crash_behavior_np(&attrs, flags);
49 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_set_crash_behavior_np");
50
51
52 if (should_wait) {
53 // For the purpose of the test we set the deadline to be now to avoid
54 // making the test wait
55 ret = posix_spawnattr_set_crash_behavior_deadline_np(&attrs, deadline, flags);
56 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_set_crash_behavior_deadline_np: %lld", deadline);
57 }
58
59 child_args[0] = test_program;
60 child_args[1] = child_mode;
61 child_args[2] = NULL;
62
63 pid_t child_pid = 0;
64 ret = posix_spawn(&child_pid, child_args[0], NULL, &attrs, &child_args[0], environ);
65 T_ASSERT_POSIX_ZERO(ret, "posix_spawn");
66 posix_spawnattr_destroy(&attrs);
67
68 if (should_wait) {
69 while (mach_continuous_time() <= deadline) {
70 usleep(1);
71 }
72 }
73
74 if (signal != 0) {
75 ret = kill(child_pid, signal);
76 T_ASSERT_POSIX_SUCCESS(ret, "kill(%d, %d)", child_pid, signal);
77 }
78
79 if (reason) {
80 ret = terminate_with_reason(child_pid, OS_REASON_TEST, TEST_REASON_CODE,
81 "Test forcing crash", OS_REASON_FLAG_CONSISTENT_FAILURE | OS_REASON_FLAG_NO_CRASH_REPORT);
82 T_ASSERT_POSIX_SUCCESS(ret, "terminate_with_reason(%d)", child_pid);
83 } else if (reason_signal) {
84 ret = terminate_with_reason(child_pid, OS_REASON_SIGNAL, SIGABRT,
85 "Test forcing crash with signal", OS_REASON_FLAG_CONSISTENT_FAILURE | OS_REASON_FLAG_NO_CRASH_REPORT);
86 T_ASSERT_POSIX_SUCCESS(ret, "terminate_with_reason_signal(%d)", child_pid);
87 }
88
89 if (dirty) {
90 ret = proc_set_dirty(child_pid, true);
91 T_ASSERT_POSIX_SUCCESS(ret, "proc_set_dirty(%d)", child_pid);
92 }
93
94 if (shutdown) {
95 ret = proc_terminate(child_pid, &signal);
96 T_ASSERT_POSIX_SUCCESS(ret, "proc_terminate(%d, %d)", child_pid, signal);
97 }
98
99 // 2. Wait for the child to exit
100 int child_status;
101 ret = wait4(-1, &child_status, 0, NULL);
102 T_ASSERT_POSIX_SUCCESS(ret, "wait4");
103
104 // 3. Check if we would have panic'ed
105 would_panic = 0;
106 length = sizeof(would_panic);
107 ret = sysctlbyname(SYSCTL_CRASH_BEHAVIOR_WOULD_PANIC, &would_panic, &length, NULL, 0);
108 T_ASSERT_POSIX_SUCCESS(ret, "SYSCTL_CRASH_BEHAVIOR_WOULD_PANIC");
109
110 T_EXPECT_EQ(would_panic, expect_panic, NULL);
111 }
112
113 T_DECL(set_crash_behavior_panic_on_crash_with_crash,
114 "set_crash_behavior_panic_on_crash_with_crash",
115 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
116 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
117 T_META_TAG_VM_PREFERRED) {
118 _do_set_crash_behavior_test("crash", 0, POSIX_SPAWN_PANIC_ON_CRASH, true);
119 }
120
121 T_DECL(set_crash_behavior_panic_on_crash_with_exit,
122 "set_crash_behavior_panic_on_crash_with_exit",
123 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
124 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
125 T_META_TAG_VM_PREFERRED) {
126 _do_set_crash_behavior_test("exit", 0, POSIX_SPAWN_PANIC_ON_CRASH, false);
127 }
128
129 T_DECL(set_crash_behavior_panic_on_crash_with_success,
130 "set_crash_behavior_panic_on_crash_with_success",
131 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
132 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
133 T_META_TAG_VM_PREFERRED) {
134 _do_set_crash_behavior_test("success", 0, POSIX_SPAWN_PANIC_ON_CRASH, false);
135 }
136
137 T_DECL(set_crash_behavior_panic_on_nonzero_with_crash,
138 "set_crash_behavior_panic_on_nonzero_with_crash",
139 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
140 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
141 T_META_TAG_VM_PREFERRED) {
142 _do_set_crash_behavior_test("crash", 0, POSIX_SPAWN_PANIC_ON_NON_ZERO_EXIT, false);
143 }
144
145 T_DECL(set_crash_behavior_panic_on_nonzero_with_exit,
146 "set_crash_behavior_panic_on_nonzero_with_exit",
147 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
148 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
149 T_META_TAG_VM_PREFERRED) {
150 _do_set_crash_behavior_test("exit", 0, POSIX_SPAWN_PANIC_ON_NON_ZERO_EXIT, true);
151 }
152
153 T_DECL(set_crash_behavior_panic_on_nonzero_with_success,
154 "set_crash_behavior_panic_on_nonzero_with_success",
155 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
156 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
157 T_META_TAG_VM_PREFERRED) {
158 _do_set_crash_behavior_test("success", 0, POSIX_SPAWN_PANIC_ON_NON_ZERO_EXIT, false);
159 }
160
161 T_DECL(set_crash_behavior_panic_on_crash_cancelled,
162 "set_crash_behavior_panic_on_crash_cancelled",
163 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
164 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
165 T_META_TAG_VM_PREFERRED) {
166 _do_set_crash_behavior_test("wait", SIGUSR1, POSIX_SPAWN_PANIC_ON_CRASH, false);
167 }
168
169 T_DECL(set_crash_behavior_panic_on_crash_sigterm,
170 "set_crash_behavior_panic_on_crash_sigterm",
171 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
172 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
173 T_META_TAG_VM_PREFERRED) {
174 _do_set_crash_behavior_test("spin", SIGTERM, POSIX_SPAWN_PANIC_ON_CRASH, false);
175 }
176
177 T_DECL(set_crash_behavior_panic_on_crash_sigkill,
178 "set_crash_behavior_panic_on_crash_sigkill",
179 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
180 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
181 T_META_TAG_VM_PREFERRED) {
182 _do_set_crash_behavior_test("spin", SIGKILL, POSIX_SPAWN_PANIC_ON_CRASH, false);
183 }
184
185 T_DECL(set_crash_behavior_panic_on_crash_terminate_with_reason,
186 "set_crash_behavior_panic_on_crash_terminate_with_reason",
187 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
188 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
189 T_META_TAG_VM_PREFERRED) {
190 _do_set_crash_behavior_test("reason", 0, POSIX_SPAWN_PANIC_ON_CRASH, true);
191 }
192
193 T_DECL(set_crash_behavior_panic_on_crash_terminate_with_reason_signal,
194 "set_crash_behavior_panic_on_crash_terminate_with_reason_signal",
195 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
196 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
197 T_META_TAG_VM_PREFERRED) {
198 _do_set_crash_behavior_test("reason_signal", 0, POSIX_SPAWN_PANIC_ON_CRASH, true);
199 }
200
201 T_DECL(set_crash_behavior_panic_on_crash_proc_terminate_clean,
202 "set_crash_behavior_panic_on_crash_proc_terminate_clean",
203 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
204 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
205 T_META_TAG_VM_PREFERRED) {
206 _do_set_crash_behavior_test("clean", 0, POSIX_SPAWN_PANIC_ON_CRASH, false);
207 }
208
209 T_DECL(set_crash_behavior_panic_on_crash_proc_terminate_dirty,
210 "set_crash_behavior_panic_on_crash_proc_terminate_dirty",
211 T_META_SYSCTL_INT(SYSCTL_CRASH_BEHAVIOR_TEST_MODE),
212 T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
213 T_META_TAG_VM_PREFERRED) {
214 _do_set_crash_behavior_test("dirty", 0, POSIX_SPAWN_PANIC_ON_CRASH, false);
215 }
216