This is xnu-11215.1.10. See this file in:
#include <signal.h>
#include <spawn_private.h>
#include <sys/coalition.h>
#include <sys/types.h>
#include <sys/spawn_internal.h>
#include <sys/sysctl.h>
#include <sys/kern_memorystatus.h>
#include <mach/coalition.h>
#include <mach-o/dyld.h>
#include <TargetConditionals.h>
#include <darwintest.h>
#include <darwintest_utils.h>
T_GLOBAL_META(
T_META_NAMESPACE("xnu.vm"),
T_META_RADAR_COMPONENT_NAME("xnu"),
T_META_RADAR_COMPONENT_VERSION("VM"),
T_META_ENABLED(TARGET_OS_IOS)
);
T_HELPER_DECL(helper, "Dummy helper")
{
exit(0);
}
static pid_t
get_coalition_leader(pid_t p)
{
static const size_t kMaxPids = 500;
int ret;
int pid_list[kMaxPids];
size_t pid_list_size = sizeof(pid_list);
int iparam[3];
#define p_type iparam[0]
#define p_order iparam[1]
#define p_pid iparam[2]
p_type = COALITION_TYPE_JETSAM;
p_order = COALITION_SORT_DEFAULT;
p_pid = p;
ret = sysctlbyname("kern.coalition_pid_list", pid_list, &pid_list_size, iparam, sizeof(iparam));
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.coalition_pid_list");
T_QUIET; T_ASSERT_LE(pid_list_size, kMaxPids * sizeof(int), "coalition is small enough");
for (size_t i = 0; i < pid_list_size / sizeof(int); i++) {
int curr_pid = pid_list[i];
int roles[COALITION_NUM_TYPES] = {};
size_t roles_size = sizeof(roles);
ret = sysctlbyname("kern.coalition_roles", roles, &roles_size, &curr_pid, sizeof(curr_pid));
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.coalition_roles");
if (roles[COALITION_TYPE_JETSAM] == COALITION_TASKROLE_LEADER) {
return curr_pid;
}
}
T_FAIL("No leader in coalition!");
return 0;
}
static pid_t child_pid = 0;
static uint64_t resource_coalition_id = 0;
static uint64_t jetsam_coalition_id = 0;
static void
continue_child_and_wait_for_exit()
{
int ret, stat;
/* Resume the child and wait for it to exit. It should exit cleanly. */
ret = kill(child_pid, SIGCONT);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to send SIGCONT to child process");
ret = waitpid(child_pid, &stat, 0);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "waitpid");
T_QUIET; T_ASSERT_TRUE(WIFEXITED(stat), "child exited.");
T_QUIET; T_ASSERT_EQ(WEXITSTATUS(stat), 0, "child exited cleanly.");
}
static int original_unrestrict_coalitions_val;
static void
unrestrict_coalitions()
{
int ret, val = 1;
size_t val_size = sizeof(val);
size_t original_unrestrict_coalitions_size = sizeof(original_unrestrict_coalitions_val);
ret = sysctlbyname("kern.unrestrict_coalitions", &original_unrestrict_coalitions_val, &original_unrestrict_coalitions_size, &val, val_size);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "unrestrict_coalitions");
}
static void
reset_unrestrict_coalitions()
{
size_t size = sizeof(original_unrestrict_coalitions_val);
int ret = sysctlbyname("kern.unrestrict_coalitions", NULL, NULL, &original_unrestrict_coalitions_val, size);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "unrestrict_coalitions");
}
static uint64_t
create_coalition(int type)
{
uint64_t id = 0;
uint32_t flags = 0;
uint64_t param[2];
int ret;
COALITION_CREATE_FLAGS_SET_TYPE(flags, type);
ret = coalition_create(&id, flags);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "coalition_create");
T_QUIET; T_ASSERT_GE(id, 0ULL, "coalition_create returned a valid id");
/* disable notifications for this coalition so launchd doesn't freak out */
param[0] = id;
param[1] = 0;
ret = sysctlbyname("kern.coalition_notify", NULL, NULL, param, sizeof(param));
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.coalition_notify");
return id;
}
static void
terminate_and_reap_coalition(uint64_t coalition_id)
{
int ret = 0;
ret = coalition_terminate(coalition_id, 0);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "coalition_terminate");
ret = coalition_reap(coalition_id, 0);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "coalition_reap");
}
static void
terminate_and_reap_coalitions()
{
terminate_and_reap_coalition(jetsam_coalition_id);
terminate_and_reap_coalition(resource_coalition_id);
}
/*
* Spawns the given command as the leader of the given coalitions.
* Process will start in a stopped state (waiting for SIGCONT)
*/
static pid_t
spawn_coalition_leader(const char *path, char *const *argv, uint64_t resource_coal_id, uint64_t jetsam_coal_id)
{
int ret;
posix_spawnattr_t attr;
extern char **environ;
pid_t new_pid = 0;
short spawn_flags = POSIX_SPAWN_START_SUSPENDED;
ret = posix_spawnattr_init(&attr);
T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_init failed with %s", strerror(ret));
ret = posix_spawnattr_setcoalition_np(&attr, jetsam_coal_id,
COALITION_TYPE_JETSAM, COALITION_TASKROLE_LEADER);
T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_setcoalition_np failed with %s", strerror(ret));
ret = posix_spawnattr_setcoalition_np(&attr, resource_coal_id,
COALITION_TYPE_RESOURCE, COALITION_TASKROLE_LEADER);
T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_setcoalition_np failed with %s", strerror(ret));
ret = posix_spawnattr_setflags(&attr, spawn_flags);
T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_setflags failed with %s", strerror(ret));
ret = posix_spawn(&new_pid, path, NULL, &attr, argv, environ);
T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawn failed with %s", strerror(ret));
ret = posix_spawnattr_destroy(&attr);
T_QUIET; T_ASSERT_EQ(ret, 0, "posix_spawnattr_destroy failed with %s\n", strerror(ret));
return new_pid;
}
T_DECL(mark_coalition_swappable, "Set coalition is swappable",
T_META_ASROOT(true),
T_META_BOOTARGS_SET("kern.swap_all_apps=1"),
T_META_TAG_VM_PREFERRED)
{
char testpath[PATH_MAX];
uint32_t testpath_buf_size;
int ret = 0;
pid_t leader_pid;
unrestrict_coalitions();
T_ATEND(reset_unrestrict_coalitions);
resource_coalition_id = create_coalition(COALITION_TYPE_RESOURCE);
jetsam_coalition_id = create_coalition(COALITION_TYPE_JETSAM);
T_ATEND(terminate_and_reap_coalitions);
testpath_buf_size = sizeof(testpath);
ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
char *const args[] = {
testpath,
"-n",
"helper",
NULL
};
child_pid = spawn_coalition_leader(testpath, args, resource_coalition_id, jetsam_coalition_id);
T_ATEND(continue_child_and_wait_for_exit);
ret = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_COALITION_IS_SWAPPABLE, child_pid, 0, NULL, 0);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_CMD_GET_PROCESS_COALITION_IS_SWAPPABLE");
T_QUIET; T_ASSERT_EQ(ret, 0, "process is not swappable at launch");
leader_pid = get_coalition_leader(child_pid);
ret = memorystatus_control(MEMORYSTATUS_CMD_MARK_PROCESS_COALITION_SWAPPABLE, leader_pid, 0, NULL, 0);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_CMD_MARK_PROCESS_COALITION_SWAPPABLE");
ret = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_COALITION_IS_SWAPPABLE, child_pid, 0, NULL, 0);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_CMD_GET_PROCESS_COALITION_IS_SWAPPABLE");
T_QUIET; T_ASSERT_EQ(ret, 1, "process is swappable");
}