+#if CONFIG_COALITIONS
+static inline void
+spawn_coalitions_release_all(coalition_t coal[COALITION_NUM_TYPES])
+{
+ for (int c = 0; c < COALITION_NUM_TYPES; c++) {
+ if (coal[c]) {
+ coalition_remove_active(coal[c]);
+ coalition_release(coal[c]);
+ }
+ }
+}
+#endif
+
+#if CONFIG_PERSONAS
+static int
+spawn_validate_persona(struct _posix_spawn_persona_info *px_persona)
+{
+ int error = 0;
+ struct persona *persona = NULL;
+ int verify = px_persona->pspi_flags & POSIX_SPAWN_PERSONA_FLAGS_VERIFY;
+
+ if (!IOTaskHasEntitlement(current_task(), PERSONA_MGMT_ENTITLEMENT)) {
+ return EPERM;
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GROUPS) {
+ if (px_persona->pspi_ngroups > NGROUPS_MAX) {
+ return EINVAL;
+ }
+ }
+
+ persona = persona_lookup(px_persona->pspi_id);
+ if (!persona) {
+ error = ESRCH;
+ goto out;
+ }
+
+ if (verify) {
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_UID) {
+ if (px_persona->pspi_uid != persona_get_uid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GID) {
+ if (px_persona->pspi_gid != persona_get_gid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GROUPS) {
+ unsigned ngroups = 0;
+ gid_t groups[NGROUPS_MAX];
+
+ if (persona_get_groups(persona, &ngroups, groups,
+ px_persona->pspi_ngroups) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ if (ngroups != px_persona->pspi_ngroups) {
+ error = EINVAL;
+ goto out;
+ }
+ while (ngroups--) {
+ if (px_persona->pspi_groups[ngroups] != groups[ngroups]) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_gmuid != persona_get_gmuid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (persona) {
+ persona_put(persona);
+ }
+
+ return error;
+}
+
+static int
+spawn_persona_adopt(proc_t p, struct _posix_spawn_persona_info *px_persona)
+{
+ int ret;
+ kauth_cred_t cred;
+ struct persona *persona = NULL;
+ int override = !!(px_persona->pspi_flags & POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
+
+ if (!override) {
+ return persona_proc_adopt_id(p, px_persona->pspi_id, NULL);
+ }
+
+ /*
+ * we want to spawn into the given persona, but we want to override
+ * the kauth with a different UID/GID combo
+ */
+ persona = persona_lookup(px_persona->pspi_id);
+ if (!persona) {
+ return ESRCH;
+ }
+
+ cred = persona_get_cred(persona);
+ if (!cred) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_UID) {
+ cred = kauth_cred_setresuid(cred,
+ px_persona->pspi_uid,
+ px_persona->pspi_uid,
+ px_persona->pspi_uid,
+ KAUTH_UID_NONE);
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GID) {
+ cred = kauth_cred_setresgid(cred,
+ px_persona->pspi_gid,
+ px_persona->pspi_gid,
+ px_persona->pspi_gid);
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GROUPS) {
+ cred = kauth_cred_setgroups(cred,
+ px_persona->pspi_groups,
+ px_persona->pspi_ngroups,
+ px_persona->pspi_gmuid);
+ }
+
+ ret = persona_proc_adopt(p, persona, cred);
+
+out:
+ persona_put(persona);
+ return ret;
+}
+#endif
+
+#if __arm64__
+extern int legacy_footprint_entitlement_mode;
+static inline void
+proc_legacy_footprint_entitled(proc_t p, task_t task, const char *caller)
+{
+#pragma unused(p, caller)
+ boolean_t legacy_footprint_entitled;
+
+ switch (legacy_footprint_entitlement_mode) {
+ case LEGACY_FOOTPRINT_ENTITLEMENT_IGNORE:
+ /* the entitlement is ignored */
+ break;
+ case LEGACY_FOOTPRINT_ENTITLEMENT_IOS11_ACCT:
+ /* the entitlement grants iOS11 legacy accounting */
+ legacy_footprint_entitled = IOTaskHasEntitlement(task,
+ "com.apple.private.memory.legacy_footprint");
+ if (legacy_footprint_entitled) {
+ task_set_legacy_footprint(task);
+ }
+ break;
+ case LEGACY_FOOTPRINT_ENTITLEMENT_LIMIT_INCREASE:
+ /* the entitlement grants a footprint limit increase */
+ legacy_footprint_entitled = IOTaskHasEntitlement(task,
+ "com.apple.private.memory.legacy_footprint");
+ if (legacy_footprint_entitled) {
+ task_set_extra_footprint_limit(task);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void
+proc_ios13extended_footprint_entitled(proc_t p, task_t task, const char *caller)
+{
+#pragma unused(p, caller)
+ boolean_t ios13extended_footprint_entitled;
+
+ /* the entitlement grants a footprint limit increase */
+ ios13extended_footprint_entitled = IOTaskHasEntitlement(task,
+ "com.apple.developer.memory.ios13extended_footprint");
+ if (ios13extended_footprint_entitled) {
+ task_set_ios13extended_footprint_limit(task);
+ }
+}
+#endif /* __arm64__ */
+
+/*
+ * Apply a modification on the proc's kauth cred until it converges.
+ *
+ * `update` consumes its argument to return a new kauth cred.
+ */
+static void
+apply_kauth_cred_update(proc_t p,
+ kauth_cred_t (^update)(kauth_cred_t orig_cred))
+{
+ kauth_cred_t my_cred, my_new_cred;
+
+ my_cred = kauth_cred_proc_ref(p);
+ for (;;) {
+ my_new_cred = update(my_cred);
+ if (my_cred == my_new_cred) {
+ kauth_cred_unref(&my_new_cred);
+ break;
+ }
+
+ /* try update cred on proc */
+ proc_ucred_lock(p);
+
+ if (p->p_ucred == my_cred) {
+ /* base pointer didn't change, donate our ref */
+ p->p_ucred = my_new_cred;
+ PROC_UPDATE_CREDS_ONPROC(p);
+ proc_ucred_unlock(p);
+
+ /* drop p->p_ucred reference */
+ kauth_cred_unref(&my_cred);
+ break;
+ }
+
+ /* base pointer changed, retry */
+ my_cred = p->p_ucred;
+ kauth_cred_ref(my_cred);
+ proc_ucred_unlock(p);
+
+ kauth_cred_unref(&my_new_cred);
+ }
+}
+
+static int
+spawn_posix_cred_adopt(proc_t p,
+ struct _posix_spawn_posix_cred_info *px_pcred_info)
+{
+ int error = 0;
+
+ if (px_pcred_info->pspci_flags & POSIX_SPAWN_POSIX_CRED_GID) {
+ struct setgid_args args = {
+ .gid = px_pcred_info->pspci_gid,
+ };
+ error = setgid(p, &args, NULL);
+ if (error) {
+ return error;
+ }
+ }
+
+ if (px_pcred_info->pspci_flags & POSIX_SPAWN_POSIX_CRED_GROUPS) {
+ error = setgroups_internal(p,
+ px_pcred_info->pspci_ngroups,
+ px_pcred_info->pspci_groups,
+ px_pcred_info->pspci_gmuid);
+ if (error) {
+ return error;
+ }
+ }
+
+ if (px_pcred_info->pspci_flags & POSIX_SPAWN_POSIX_CRED_UID) {
+ struct setuid_args args = {
+ .uid = px_pcred_info->pspci_uid,
+ };
+ error = setuid(p, &args, NULL);
+ if (error) {
+ return error;
+ }
+ }
+ return 0;
+}
+
+/*
+ * posix_spawn
+ *
+ * Parameters: uap->pid Pointer to pid return area
+ * uap->fname File name to exec
+ * uap->argp Argument list
+ * uap->envp Environment list