+
+
+static int
+proc_piduuidinfo(pid_t pid, uuid_t uuid_buf, uint32_t buffersize)
+{
+ struct proc * p = PROC_NULL;
+ int zombref = 0;
+
+ if (buffersize < sizeof(uuid_t)) {
+ return EINVAL;
+ }
+
+ if ((p = proc_find(pid)) == PROC_NULL) {
+ p = proc_find_zombref(pid);
+ zombref = 1;
+ }
+ if (p == PROC_NULL) {
+ return ESRCH;
+ }
+
+ proc_getexecutableuuid(p, (unsigned char *)uuid_buf, buffersize);
+
+ if (zombref) {
+ proc_drop_zombref(p);
+ } else {
+ proc_rele(p);
+ }
+
+ return 0;
+}
+
+/*
+ * Function to get the uuid and pid of the originator of the voucher.
+ */
+int
+proc_pidoriginatorpid_uuid(uuid_t uuid, uint32_t buffersize, pid_t *pid)
+{
+ pid_t originator_pid;
+ kern_return_t kr;
+ int error;
+
+ /*
+ * Get the current voucher origin pid. The pid returned here
+ * might not be valid or may have been recycled.
+ */
+ kr = thread_get_current_voucher_origin_pid(&originator_pid);
+ /* If errors, convert errors to appropriate format */
+ if (kr) {
+ if (kr == KERN_INVALID_TASK) {
+ error = ESRCH;
+ } else if (kr == KERN_INVALID_VALUE) {
+ error = ENOATTR;
+ } else {
+ error = EINVAL;
+ }
+ return error;
+ }
+
+ *pid = originator_pid;
+ error = proc_piduuidinfo(originator_pid, uuid, buffersize);
+ return error;
+}
+
+/*
+ * Function to get the uuid of the originator of the voucher.
+ */
+int
+proc_pidoriginatoruuid(uuid_t uuid, uint32_t buffersize)
+{
+ pid_t originator_pid;
+ return proc_pidoriginatorpid_uuid(uuid, buffersize, &originator_pid);
+}
+
+/*
+ * Function to get the task ipc table size.
+ */
+int
+proc_pidipctableinfo(proc_t p, struct proc_ipctableinfo *table_info)
+{
+ task_t task;
+ int error = 0;
+
+ task = p->task;
+
+ bzero(table_info, sizeof(struct proc_ipctableinfo));
+ error = fill_taskipctableinfo(task, &(table_info->table_size), &(table_info->table_free));
+
+ if (error) {
+ error = EINVAL;
+ }
+
+ return error;
+}
+
+/***************************** proc_pidoriginatorinfo ***************************/
+
+int
+proc_pidoriginatorinfo(int pid, int flavor, user_addr_t buffer, uint32_t buffersize, int32_t * retval)
+{
+ int error = ENOTSUP;
+ uint32_t size;
+
+ switch (flavor) {
+ case PROC_PIDORIGINATOR_UUID:
+ size = PROC_PIDORIGINATOR_UUID_SIZE;
+ break;
+ case PROC_PIDORIGINATOR_BGSTATE:
+ size = PROC_PIDORIGINATOR_BGSTATE_SIZE;
+ break;
+ case PROC_PIDORIGINATOR_PID_UUID:
+ size = PROC_PIDORIGINATOR_PID_UUID_SIZE;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (buffersize < size) {
+ return ENOMEM;
+ }
+
+ if (pid != 0 && pid != proc_selfpid()) {
+ return EINVAL;
+ }
+
+ switch (flavor) {
+ case PROC_PIDORIGINATOR_UUID: {
+ uuid_t uuid = {};
+
+ error = proc_pidoriginatoruuid(uuid, sizeof(uuid));
+ if (error != 0) {
+ goto out;
+ }
+
+ error = copyout(uuid, buffer, size);
+ if (error == 0) {
+ *retval = size;
+ }
+ }
+ break;
+
+ case PROC_PIDORIGINATOR_PID_UUID: {
+ struct proc_originatorinfo originator_info;
+ bzero(&originator_info, sizeof(originator_info));
+
+ error = proc_pidoriginatorpid_uuid(originator_info.originator_uuid,
+ sizeof(uuid_t), &originator_info.originator_pid);
+ if (error != 0) {
+ goto out;
+ }
+
+ error = copyout(&originator_info, buffer, size);
+ if (error == 0) {
+ *retval = size;
+ }
+ }
+ break;
+
+ case PROC_PIDORIGINATOR_BGSTATE: {
+ uint32_t is_backgrounded = 0;
+ error = proc_get_originatorbgstate(&is_backgrounded);
+ if (error) {
+ goto out;
+ }
+
+ error = copyout(&is_backgrounded, buffer, size);
+ if (error == 0) {
+ *retval = size;
+ }
+ }
+ break;
+
+ default:
+ error = ENOTSUP;
+ }
+out:
+ return error;
+}
+
+/***************************** proc_listcoalitions ***************************/
+int
+proc_listcoalitions(int flavor, int type, user_addr_t buffer,
+ uint32_t buffersize, int32_t *retval)
+{
+#if CONFIG_COALITIONS
+ int error = ENOTSUP;
+ int coal_type;
+ uint32_t elem_size;
+ void *coalinfo = NULL;
+ uint32_t k_buffersize = 0, copyout_sz = 0;
+ int ncoals = 0, ncoals_ = 0;
+
+ /* struct procinfo_coalinfo; */
+
+ switch (flavor) {
+ case LISTCOALITIONS_ALL_COALS:
+ elem_size = LISTCOALITIONS_ALL_COALS_SIZE;
+ coal_type = -1;
+ break;
+ case LISTCOALITIONS_SINGLE_TYPE:
+ elem_size = LISTCOALITIONS_SINGLE_TYPE_SIZE;
+ coal_type = type;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ /* find the total number of coalitions */
+ ncoals = coalitions_get_list(coal_type, NULL, 0);
+
+ if (ncoals == 0 || buffer == 0 || buffersize == 0) {
+ /*
+ * user just wants buffer size
+ * or there are no coalitions
+ */
+ error = 0;
+ *retval = (int)(ncoals * elem_size);
+ goto out;
+ }
+
+ k_buffersize = ncoals * elem_size;
+ coalinfo = kheap_alloc(KHEAP_TEMP, k_buffersize, Z_WAITOK | Z_ZERO);
+ if (!coalinfo) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ switch (flavor) {
+ case LISTCOALITIONS_ALL_COALS:
+ case LISTCOALITIONS_SINGLE_TYPE:
+ ncoals_ = coalitions_get_list(coal_type, coalinfo, ncoals);
+ break;
+ default:
+ panic("memory corruption?!");
+ }
+
+ if (ncoals_ == 0) {
+ /* all the coalitions disappeared... weird but valid */
+ error = 0;
+ *retval = 0;
+ goto out;
+ }
+
+ /*
+ * Some coalitions may have disappeared between our initial check,
+ * and the the actual list acquisition.
+ * Only copy out what we really need.
+ */
+ copyout_sz = k_buffersize;
+ if (ncoals_ < ncoals) {
+ copyout_sz = ncoals_ * elem_size;
+ }
+
+ /*
+ * copy the list up to user space
+ * (we're guaranteed to have a non-null pointer/size here)
+ */
+ error = copyout(coalinfo, buffer,
+ copyout_sz < buffersize ? copyout_sz : buffersize);
+
+ if (error == 0) {
+ *retval = (int)copyout_sz;
+ }
+
+out:
+ if (coalinfo) {
+ kheap_free(KHEAP_TEMP, coalinfo, k_buffersize);
+ }
+
+ return error;
+#else
+ /* no coalition support */
+ (void)flavor;
+ (void)type;
+ (void)buffer;
+ (void)buffersize;
+ (void)retval;
+ return ENOTSUP;
+#endif
+}
+
+
+/*************************** proc_can_use_forgeound_hw **************************/
+int
+proc_can_use_foreground_hw(int pid, user_addr_t u_reason, uint32_t reasonsize, int32_t *retval)
+{
+ proc_t p = PROC_NULL;
+ int error = 0;
+ uint32_t reason = PROC_FGHW_ERROR;
+ uint32_t isBG = 0;
+ task_t task = TASK_NULL;
+#if CONFIG_COALITIONS
+ coalition_t coal = COALITION_NULL;
+#endif
+
+ *retval = 0;
+
+ if (pid <= 0) {
+ error = EINVAL;
+ reason = PROC_FGHW_ERROR;
+ goto out;
+ }
+
+ p = proc_find(pid);
+ if (p == PROC_NULL) {
+ error = ESRCH;
+ reason = PROC_FGHW_ERROR;
+ goto out;
+ }
+
+#if CONFIG_COALITIONS
+ if (p != current_proc() &&
+ !kauth_cred_issuser(kauth_cred_get())) {
+ error = EPERM;
+ reason = PROC_FGHW_ERROR;
+ goto out;
+ }
+
+ task = p->task;
+ if (coalition_is_leader(task, task_get_coalition(task, COALITION_TYPE_JETSAM))) {
+ task_reference(task);
+ } else {
+ /* current task is not a coalition leader: find the leader */
+ task = coalition_get_leader(coal);
+ }
+
+ if (task != TASK_NULL) {
+ /*
+ * If task is non-null, then it is the coalition leader of the
+ * current process' coalition. This could be the same task as
+ * the current_task, and that's OK.
+ */
+ uint32_t flags = 0;
+ int role;
+
+ proc_get_darwinbgstate(task, &flags);
+ if ((flags & PROC_FLAG_APPLICATION) != PROC_FLAG_APPLICATION) {
+ /*
+ * Coalition leader is not an application, continue
+ * searching for other ways this task could gain
+ * access to HW
+ */
+ reason = PROC_FGHW_DAEMON_LEADER;
+ goto no_leader;
+ }
+
+ if (proc_get_effective_task_policy(task, TASK_POLICY_DARWIN_BG)) {
+ /*
+ * If the leader of the current process' coalition has
+ * been marked as DARWIN_BG, then it definitely should
+ * not be using foreground hardware resources.
+ */
+ reason = PROC_FGHW_LEADER_BACKGROUND;
+ goto out;
+ }
+
+ role = proc_get_effective_task_policy(task, TASK_POLICY_ROLE);
+ switch (role) {
+ case TASK_FOREGROUND_APPLICATION: /* DARWIN_ROLE_UI_FOCAL */
+ case TASK_BACKGROUND_APPLICATION: /* DARWIN_ROLE_UI */
+ /*
+ * The leader of this coalition is a focal, UI app:
+ * access granted
+ * TODO: should extensions/plugins be allowed to use
+ * this hardware?
+ */
+ *retval = 1;
+ reason = PROC_FGHW_OK;
+ goto out;
+ case TASK_DEFAULT_APPLICATION: /* DARWIN_ROLE_UI_NON_FOCAL */
+ case TASK_NONUI_APPLICATION: /* DARWIN_ROLE_NON_UI */
+ case TASK_THROTTLE_APPLICATION:
+ case TASK_UNSPECIFIED:
+ default:
+ /* non-focal, non-ui apps don't get access */
+ reason = PROC_FGHW_LEADER_NONUI;
+ goto out;
+ }
+ }
+
+no_leader:
+ if (task != TASK_NULL) {
+ task_deallocate(task);
+ task = TASK_NULL;
+ }
+#endif /* CONFIG_COALITIONS */
+
+ /*
+ * There is no reasonable semantic to investigate the currently
+ * adopted voucher of an arbitrary thread in a non-current process.
+ * We return '0'
+ */
+ if (p != current_proc()) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * In the absence of coalitions, fall back to a voucher-based lookup
+ * where a daemon can used foreground HW if it's operating on behalf
+ * of a foreground application.
+ * NOTE: this is equivalent to a call to
+ * proc_pidoriginatorinfo(PROC_PIDORIGINATOR_BGSTATE, &isBG, sizeof(isBG))
+ */
+ isBG = 1;
+ error = proc_get_originatorbgstate(&isBG);
+ switch (error) {
+ case 0:
+ break;
+ case ESRCH:
+ reason = PROC_FGHW_NO_ORIGINATOR;
+ error = 0;
+ goto out;
+ case ENOATTR:
+ reason = PROC_FGHW_NO_VOUCHER_ATTR;
+ error = 0;
+ goto out;
+ case EINVAL:
+ reason = PROC_FGHW_DAEMON_NO_VOUCHER;
+ error = 0;
+ goto out;
+ default:
+ /* some other error occurred: report that to the caller */
+ reason = PROC_FGHW_VOUCHER_ERROR;
+ goto out;
+ }
+
+ if (isBG) {
+ reason = PROC_FGHW_ORIGINATOR_BACKGROUND;
+ error = 0;
+ } else {
+ /*
+ * The process itself is either a foreground app, or has
+ * adopted a voucher originating from an app that's still in
+ * the foreground
+ */
+ reason = PROC_FGHW_DAEMON_OK;
+ *retval = 1;
+ }
+
+out:
+ if (task != TASK_NULL) {
+ task_deallocate(task);
+ }
+ if (p != PROC_NULL) {
+ proc_rele(p);
+ }
+ if (reasonsize >= sizeof(reason) && u_reason != (user_addr_t)0) {
+ (void)copyout(&reason, u_reason, sizeof(reason));
+ }
+ return error;
+}
+
+