xref: /xnu-11215.41.3/tests/coalition_policy.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1 /*
2  * Copyright (c) 2024 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 #include <sys/coalition_private.h>
30 
31 #include <mach/coalition.h>
32 #include <sys/coalition.h>
33 #include <libproc.h>
34 
35 #include <sys/types.h>
36 #include <unistd.h>
37 
38 #include <darwintest.h>
39 #include <darwintest_utils.h>
40 #include <mach/task.h>
41 #include <mach/task_policy.h>
42 #include <mach/mach.h>
43 
44 T_GLOBAL_META(T_META_NAMESPACE("xnu.scheduler"),
45     T_META_RADAR_COMPONENT_NAME("xnu"),
46     T_META_RADAR_COMPONENT_VERSION("scheduler"),
47     T_META_OWNER("chimene"),
48     T_META_RUN_CONCURRENTLY(false));
49 
50 static uint64_t
get_jet_id(void)51 get_jet_id(void)
52 {
53 	T_LOG("uid: %d, pid %d", getuid(), getpid());
54 
55 	struct proc_pidcoalitioninfo idinfo;
56 
57 	int ret = proc_pidinfo(getpid(), PROC_PIDCOALITIONINFO, 0,
58 	    &idinfo, sizeof(idinfo));
59 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc_pidinfo(... PROC_PIDCOALITIONINFO ...)");
60 
61 	uint64_t res_id = idinfo.coalition_id[COALITION_TYPE_RESOURCE];
62 	uint64_t jet_id = idinfo.coalition_id[COALITION_TYPE_JETSAM];
63 
64 	T_LOG("Resource coalition: %lld, Jetsam coalition: %lld", res_id, jet_id);
65 
66 	return jet_id;
67 }
68 
69 static void
check_is_bg(bool wants_bg)70 check_is_bg(bool wants_bg)
71 {
72 	kern_return_t kr;
73 	struct task_policy_state policy_state;
74 
75 	mach_msg_type_number_t count = TASK_POLICY_STATE_COUNT;
76 	boolean_t get_default = FALSE;
77 
78 	kr = task_policy_get(mach_task_self(), TASK_POLICY_STATE,
79 	    (task_policy_t)&policy_state, &count, &get_default);
80 
81 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "task_policy_get(TASK_POLICY_STATE)");
82 
83 	/*
84 	 * A test reporting type=APPLICATION should have the live donor bit set.
85 	 * If this fails, the test may have been launched as a daemon instead.
86 	 */
87 	T_QUIET; T_ASSERT_BITS_SET(policy_state.flags, TASK_IMP_LIVE_DONOR, "test should be live donor enabled");
88 
89 	/*
90 	 * The BG bit is updated via task_policy_update_internal_locked,
91 	 * checking this proves that the first phase update ran on this task.
92 	 */
93 	if (wants_bg) {
94 		T_ASSERT_BITS_SET(policy_state.effective, POLICY_EFF_DARWIN_BG, "%d: is BG", getpid());
95 	} else {
96 		T_ASSERT_BITS_NOTSET(policy_state.effective, POLICY_EFF_DARWIN_BG, "%d: is not BG", getpid());
97 	}
98 
99 	/*
100 	 * The live donor bit is updated via task_policy_update_complete_unlocked,
101 	 * checking this proves that the second phase update ran on this task.
102 	 */
103 	if (wants_bg) {
104 		T_ASSERT_BITS_NOTSET(policy_state.flags, TASK_IMP_DONOR, "%d: is not live donor", getpid());
105 	} else {
106 		T_ASSERT_BITS_SET(policy_state.flags, TASK_IMP_DONOR, "%d: is live donor", getpid());
107 	}
108 }
109 
110 static void
set_coalition_bg(uint64_t jet_id,bool set_bg)111 set_coalition_bg(uint64_t jet_id, bool set_bg)
112 {
113 	if (set_bg) {
114 		T_ASSERT_POSIX_SUCCESS(coalition_policy_set(jet_id, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_DARWIN_BG),
115 		    "coalition_policy_set(%lld, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_DARWIN_BG)", jet_id);
116 	} else {
117 		T_ASSERT_POSIX_SUCCESS(coalition_policy_set(jet_id, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_NONE),
118 		    "coalition_policy_set(%lld, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_NONE)", jet_id);
119 	}
120 }
121 
122 static void
log_suppress(uint64_t jet_id)123 log_suppress(uint64_t jet_id)
124 {
125 	int suppress = coalition_policy_get(jet_id, COALITION_POLICY_SUPPRESS);
126 	T_ASSERT_POSIX_SUCCESS(suppress, "coalition_policy_get(%lld, COALITION_POLICY_SUPPRESS)", jet_id);
127 	T_LOG("suppress: %d", suppress);
128 }
129 
130 static void
restore_coalition_state(void)131 restore_coalition_state(void)
132 {
133 	struct proc_pidcoalitioninfo idinfo;
134 
135 	int ret = proc_pidinfo(getpid(), PROC_PIDCOALITIONINFO, 0,
136 	    &idinfo, sizeof(idinfo));
137 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc_pidinfo(... PROC_PIDCOALITIONINFO ...)");
138 
139 	uint64_t jet_id = idinfo.coalition_id[COALITION_TYPE_JETSAM];
140 
141 	T_QUIET; T_ASSERT_POSIX_SUCCESS(coalition_policy_set(jet_id, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_NONE),
142 	    "coalition_policy_set(%lld, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_NONE)", jet_id);
143 }
144 
145 
146 T_DECL(coalition_suppress_read_entitled, "COALITION_POLICY_SUPPRESS should be readable with entitlement")
147 {
148 	T_ATEND(restore_coalition_state);
149 
150 	uint64_t jet_id = get_jet_id();
151 
152 	int suppress = coalition_policy_get(jet_id, COALITION_POLICY_SUPPRESS);
153 
154 	T_ASSERT_POSIX_SUCCESS(suppress, "coalition_policy_get(%lld, COALITION_POLICY_SUPPRESS)", jet_id);
155 
156 	T_LOG("suppress: %d", suppress);
157 }
158 
159 T_DECL(coalition_suppress_read_rsrc_coalition, "COALITION_POLICY_SUPPRESS shouldn't work on resource coalitions")
160 {
161 	T_ATEND(restore_coalition_state);
162 
163 	T_LOG("uid: %d, pid %d", getuid(), getpid());
164 
165 	struct proc_pidcoalitioninfo idinfo;
166 
167 	int ret = proc_pidinfo(getpid(), PROC_PIDCOALITIONINFO, 0,
168 	    &idinfo, sizeof(idinfo));
169 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc_pidinfo(... PROC_PIDCOALITIONINFO ...)");
170 
171 	uint64_t res_id = idinfo.coalition_id[COALITION_TYPE_RESOURCE];
172 	uint64_t jet_id = idinfo.coalition_id[COALITION_TYPE_JETSAM];
173 
174 	T_LOG("res_id: %lld, jet_id: %lld", res_id, jet_id);
175 
176 	int suppress = coalition_policy_get(res_id, COALITION_POLICY_SUPPRESS);
177 
178 	T_EXPECT_POSIX_FAILURE(suppress,
179 	    ENOTSUP, "coalition_policy_get(%lld, COALITION_POLICY_SUPPRESS)", res_id);
180 
181 	T_LOG("suppress: %d", suppress);
182 }
183 
184 T_DECL(coalition_suppress_set, "COALITION_POLICY_SUPPRESS should be settable with entitlement")
185 {
186 	T_ATEND(restore_coalition_state);
187 
188 	uint64_t jet_id = get_jet_id();
189 
190 	set_coalition_bg(jet_id, true);
191 
192 	log_suppress(jet_id);
193 
194 	set_coalition_bg(jet_id, false);
195 
196 	log_suppress(jet_id);
197 }
198 
199 T_DECL(coalition_suppress_set_check_task, "current task should become BG when coalition changes", T_META_ASROOT(true))
200 {
201 	T_ATEND(restore_coalition_state);
202 
203 	uint64_t jet_id = get_jet_id();
204 
205 	log_suppress(jet_id);
206 
207 	check_is_bg(false);
208 
209 	set_coalition_bg(jet_id, true);
210 
211 	log_suppress(jet_id);
212 
213 	check_is_bg(true);
214 
215 	set_coalition_bg(jet_id, false);
216 
217 	log_suppress(jet_id);
218 
219 	check_is_bg(false);
220 }
221 
222 T_DECL(coalition_suppress_child_bg, "child spawned into bg coalition should be bg", T_META_ASROOT(true))
223 {
224 	T_ATEND(restore_coalition_state);
225 
226 	uint64_t jet_id = get_jet_id();
227 
228 	check_is_bg(false);
229 
230 	set_coalition_bg(jet_id, true);
231 
232 	check_is_bg(true);
233 
234 	T_LOG("Spawning child");
235 
236 	pid_t child_pid = fork();
237 
238 	if (child_pid == 0) {
239 		/* child process */
240 
241 		//T_LOG("child pid %d sleeping", getpid());
242 
243 		//sleep(10000);
244 
245 		check_is_bg(true);
246 
247 		T_LOG("Exit pid %d", getpid());
248 
249 		exit(0);
250 	} else {
251 		T_ASSERT_POSIX_SUCCESS(child_pid, "fork returned, child pid %d", child_pid);
252 
253 		/* wait for child process to exit */
254 		int exit_status = 0, signum = 0;
255 
256 		T_ASSERT_TRUE(dt_waitpid(child_pid, &exit_status, &signum, 500000), /* TODO */
257 		    "wait for child (%d) complete", child_pid);
258 
259 		T_QUIET; T_ASSERT_EQ(exit_status, 0, "dt_waitpid: exit_status");
260 		T_QUIET; T_ASSERT_EQ(signum, 0, "dt_waitpid: signum");
261 	}
262 
263 	check_is_bg(true);
264 
265 	set_coalition_bg(jet_id, false);
266 
267 	check_is_bg(false);
268 }
269 
270 T_DECL(coalition_suppress_child_change_bg, "child changing coalition to bg should affect parent", T_META_ASROOT(true))
271 {
272 	T_ATEND(restore_coalition_state);
273 
274 	uint64_t jet_id = get_jet_id();
275 
276 	check_is_bg(false);
277 
278 	T_LOG("Spawning child");
279 
280 	pid_t child_pid = fork();
281 
282 	if (child_pid == 0) {
283 		/* child process */
284 
285 		check_is_bg(false);
286 
287 		set_coalition_bg(jet_id, true);
288 
289 		check_is_bg(true);
290 
291 		T_LOG("Exit pid %d", getpid());
292 
293 		exit(0);
294 	} else {
295 		T_ASSERT_POSIX_SUCCESS(child_pid, "fork returned, child pid %d", child_pid);
296 
297 		/* wait for child process to exit */
298 		int exit_status = 0, signum = 0;
299 
300 		T_ASSERT_TRUE(dt_waitpid(child_pid, &exit_status, &signum, 5),
301 		    "wait for child (%d) complete", child_pid);
302 
303 		T_QUIET; T_ASSERT_EQ(exit_status, 0, "dt_waitpid: exit_status");
304 		T_QUIET; T_ASSERT_EQ(signum, 0, "dt_waitpid: signum");
305 	}
306 
307 	check_is_bg(true);
308 
309 	set_coalition_bg(jet_id, false);
310 
311 	check_is_bg(false);
312 }
313