xref: /xnu-8796.101.5/tests/vm/app_swap.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 #include <signal.h>
2 #include <spawn_private.h>
3 #include <sys/coalition.h>
4 #include <sys/types.h>
5 #include <sys/spawn_internal.h>
6 #include <sys/sysctl.h>
7 #include <sys/kern_memorystatus.h>
8 #include <mach/coalition.h>
9 #include <mach-o/dyld.h>
10 #include <TargetConditionals.h>
11 
12 #include <darwintest.h>
13 #include <darwintest_utils.h>
14 
15 T_GLOBAL_META(
16 	T_META_NAMESPACE("xnu.vm"),
17 	T_META_RADAR_COMPONENT_NAME("xnu"),
18 	T_META_RADAR_COMPONENT_VERSION("VM"),
19 	T_META_ENABLED(TARGET_OS_IOS)
20 	);
21 
22 T_HELPER_DECL(helper, "Dummy helper")
23 {
24 	exit(0);
25 }
26 
27 static pid_t
get_coalition_leader(pid_t p)28 get_coalition_leader(pid_t p)
29 {
30 	static const size_t kMaxPids = 500;
31 	int ret;
32 	int pid_list[kMaxPids];
33 	size_t pid_list_size = sizeof(pid_list);
34 
35 	int iparam[3];
36 #define p_type  iparam[0]
37 #define p_order iparam[1]
38 #define p_pid   iparam[2]
39 	p_type = COALITION_TYPE_JETSAM;
40 	p_order = COALITION_SORT_DEFAULT;
41 	p_pid = p;
42 
43 	ret = sysctlbyname("kern.coalition_pid_list", pid_list, &pid_list_size, iparam, sizeof(iparam));
44 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.coalition_pid_list");
45 	T_QUIET; T_ASSERT_LE(pid_list_size, kMaxPids * sizeof(int), "coalition is small enough");
46 
47 	for (size_t i = 0; i < pid_list_size / sizeof(int); i++) {
48 		int curr_pid = pid_list[i];
49 		int roles[COALITION_NUM_TYPES] = {};
50 		size_t roles_size = sizeof(roles);
51 
52 		ret = sysctlbyname("kern.coalition_roles", roles, &roles_size, &curr_pid, sizeof(curr_pid));
53 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.coalition_roles");
54 		if (roles[COALITION_TYPE_JETSAM] == COALITION_TASKROLE_LEADER) {
55 			return curr_pid;
56 		}
57 	}
58 
59 	T_FAIL("No leader in coalition!");
60 	return 0;
61 }
62 
63 static pid_t child_pid = 0;
64 static uint64_t resource_coalition_id = 0;
65 static uint64_t jetsam_coalition_id = 0;
66 
67 static void
continue_child_and_wait_for_exit()68 continue_child_and_wait_for_exit()
69 {
70 	int ret, stat;
71 	/* Resume the child and wait for it to exit. It should exit cleanly. */
72 	ret = kill(child_pid, SIGCONT);
73 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to send SIGCONT to child process");
74 	ret = waitpid(child_pid, &stat, 0);
75 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "waitpid");
76 	T_QUIET; T_ASSERT_TRUE(WIFEXITED(stat), "child exited.");
77 	T_QUIET; T_ASSERT_EQ(WEXITSTATUS(stat), 0, "child exited cleanly.");
78 }
79 
80 static int original_unrestrict_coalitions_val;
81 
82 static void
unrestrict_coalitions()83 unrestrict_coalitions()
84 {
85 	int ret, val = 1;
86 	size_t val_size = sizeof(val);
87 	size_t original_unrestrict_coalitions_size = sizeof(original_unrestrict_coalitions_val);
88 	ret = sysctlbyname("kern.unrestrict_coalitions", &original_unrestrict_coalitions_val, &original_unrestrict_coalitions_size, &val, val_size);
89 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "unrestrict_coalitions");
90 }
91 
92 static void
reset_unrestrict_coalitions()93 reset_unrestrict_coalitions()
94 {
95 	size_t size = sizeof(original_unrestrict_coalitions_val);
96 	int ret = sysctlbyname("kern.unrestrict_coalitions", NULL, NULL, &original_unrestrict_coalitions_val, size);
97 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "unrestrict_coalitions");
98 }
99 
100 static uint64_t
create_coalition(int type)101 create_coalition(int type)
102 {
103 	uint64_t id = 0;
104 	uint32_t flags = 0;
105 	uint64_t param[2];
106 	int ret;
107 
108 	COALITION_CREATE_FLAGS_SET_TYPE(flags, type);
109 	ret = coalition_create(&id, flags);
110 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "coalition_create");
111 	T_QUIET; T_ASSERT_GE(id, 0ULL, "coalition_create returned a valid id");
112 
113 	/* disable notifications for this coalition so launchd doesn't freak out */
114 	param[0] = id;
115 	param[1] = 0;
116 	ret = sysctlbyname("kern.coalition_notify", NULL, NULL, param, sizeof(param));
117 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.coalition_notify");
118 
119 	return id;
120 }
121 
122 static void
terminate_and_reap_coalition(uint64_t coalition_id)123 terminate_and_reap_coalition(uint64_t coalition_id)
124 {
125 	int ret = 0;
126 	ret = coalition_terminate(coalition_id, 0);
127 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "coalition_terminate");
128 
129 	ret = coalition_reap(coalition_id, 0);
130 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "coalition_reap");
131 }
132 
133 static void
terminate_and_reap_coalitions()134 terminate_and_reap_coalitions()
135 {
136 	terminate_and_reap_coalition(jetsam_coalition_id);
137 	terminate_and_reap_coalition(resource_coalition_id);
138 }
139 
140 /*
141  * Spawns the given command as the leader of the given coalitions.
142  * Process will start in a stopped state (waiting for SIGCONT)
143  */
144 static pid_t
spawn_coalition_leader(const char * path,char * const * argv,uint64_t resource_coal_id,uint64_t jetsam_coal_id)145 spawn_coalition_leader(const char *path, char *const *argv, uint64_t resource_coal_id, uint64_t jetsam_coal_id)
146 {
147 	int ret;
148 	posix_spawnattr_t attr;
149 	extern char **environ;
150 	pid_t new_pid = 0;
151 	short spawn_flags = POSIX_SPAWN_START_SUSPENDED;
152 
153 	ret = posix_spawnattr_init(&attr);
154 	T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_init failed with %s", strerror(ret));
155 
156 	ret = posix_spawnattr_setcoalition_np(&attr, jetsam_coal_id,
157 	    COALITION_TYPE_JETSAM, COALITION_TASKROLE_LEADER);
158 	T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_setcoalition_np failed with %s", strerror(ret));
159 	ret = posix_spawnattr_setcoalition_np(&attr, resource_coal_id,
160 	    COALITION_TYPE_RESOURCE, COALITION_TASKROLE_LEADER);
161 	T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_setcoalition_np failed with %s", strerror(ret));
162 
163 	ret = posix_spawnattr_setflags(&attr, spawn_flags);
164 	T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_setflags failed with %s", strerror(ret));
165 
166 	ret = posix_spawn(&new_pid, path, NULL, &attr, argv, environ);
167 	T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawn failed with %s", strerror(ret));
168 
169 	ret = posix_spawnattr_destroy(&attr);
170 	T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_destroy failed with %s\n", strerror(ret));
171 	return new_pid;
172 }
173 
174 T_DECL(mark_coalition_swappable, "Set coalition is swappable",
175     T_META_ASROOT(true),
176     T_META_BOOTARGS_SET("kern.swap_all_apps=1"))
177 {
178 	char testpath[PATH_MAX];
179 	uint32_t testpath_buf_size;
180 	int ret = 0;
181 	pid_t leader_pid;
182 
183 	unrestrict_coalitions();
184 	T_ATEND(reset_unrestrict_coalitions);
185 
186 	resource_coalition_id = create_coalition(COALITION_TYPE_RESOURCE);
187 	jetsam_coalition_id = create_coalition(COALITION_TYPE_JETSAM);
188 	T_ATEND(terminate_and_reap_coalitions);
189 
190 	testpath_buf_size = sizeof(testpath);
191 	ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
192 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
193 
194 	char *const args[] = {
195 		testpath,
196 		"-n",
197 		"helper",
198 		NULL
199 	};
200 	child_pid = spawn_coalition_leader(testpath, args, resource_coalition_id, jetsam_coalition_id);
201 
202 	T_ATEND(continue_child_and_wait_for_exit);
203 
204 	ret = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_COALITION_IS_SWAPPABLE, child_pid, 0, NULL, 0);
205 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_CMD_GET_PROCESS_COALITION_IS_SWAPPABLE");
206 	T_QUIET; T_ASSERT_EQ(ret, 0, "process is not swappable at launch");
207 
208 	leader_pid = get_coalition_leader(child_pid);
209 
210 	ret = memorystatus_control(MEMORYSTATUS_CMD_MARK_PROCESS_COALITION_SWAPPABLE, leader_pid, 0, NULL, 0);
211 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_CMD_MARK_PROCESS_COALITION_SWAPPABLE");
212 
213 	ret = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_COALITION_IS_SWAPPABLE, child_pid, 0, NULL, 0);
214 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_CMD_GET_PROCESS_COALITION_IS_SWAPPABLE");
215 	T_QUIET; T_ASSERT_EQ(ret, 1, "process is swappable");
216 }
217