+ 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;
+}
+
+
+/********************************** proc_pidinfo ********************************/
+
+
+int
+proc_pidinfo(int pid, uint32_t flags, uint64_t ext_id, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t * retval)
+{
+ struct proc * p = PROC_NULL;
+ int error = ENOTSUP;
+ int gotref = 0;
+ int findzomb = 0;
+ int shortversion = 0;
+ uint32_t size;
+ int zombie = 0;
+ bool thuniqueid = false;
+ int uniqidversion = 0;
+ bool check_same_user;
+
+ switch (flavor) {
+ case PROC_PIDLISTFDS:
+ size = PROC_PIDLISTFD_SIZE;
+ if (buffer == USER_ADDR_NULL) {
+ size = 0;
+ }
+ break;
+ case PROC_PIDTBSDINFO:
+ size = PROC_PIDTBSDINFO_SIZE;
+ break;
+ case PROC_PIDTASKINFO:
+ size = PROC_PIDTASKINFO_SIZE;
+ break;
+ case PROC_PIDTASKALLINFO:
+ size = PROC_PIDTASKALLINFO_SIZE;
+ break;
+ case PROC_PIDTHREADINFO:
+ size = PROC_PIDTHREADINFO_SIZE;
+ break;
+ case PROC_PIDLISTTHREADIDS:
+ size = PROC_PIDLISTTHREADIDS_SIZE;
+ break;
+ case PROC_PIDLISTTHREADS:
+ size = PROC_PIDLISTTHREADS_SIZE;
+ break;
+ case PROC_PIDREGIONINFO:
+ size = PROC_PIDREGIONINFO_SIZE;
+ break;
+ case PROC_PIDREGIONPATHINFO:
+ size = PROC_PIDREGIONPATHINFO_SIZE;
+ break;
+ case PROC_PIDVNODEPATHINFO:
+ size = PROC_PIDVNODEPATHINFO_SIZE;
+ break;
+ case PROC_PIDTHREADPATHINFO:
+ size = PROC_PIDTHREADPATHINFO_SIZE;
+ break;
+ case PROC_PIDPATHINFO:
+ size = MAXPATHLEN;
+ break;
+ case PROC_PIDWORKQUEUEINFO:
+ /* kernel does not have workq info */
+ if (pid == 0) {
+ return EINVAL;
+ } else {
+ size = PROC_PIDWORKQUEUEINFO_SIZE;
+ }
+ break;
+ case PROC_PIDT_SHORTBSDINFO:
+ size = PROC_PIDT_SHORTBSDINFO_SIZE;
+ break;
+ case PROC_PIDLISTFILEPORTS:
+ size = PROC_PIDLISTFILEPORTS_SIZE;
+ if (buffer == (user_addr_t)0) {
+ size = 0;
+ }
+ break;
+ case PROC_PIDTHREADID64INFO:
+ size = PROC_PIDTHREADID64INFO_SIZE;
+ break;
+ case PROC_PIDUNIQIDENTIFIERINFO:
+ size = PROC_PIDUNIQIDENTIFIERINFO_SIZE;
+ break;
+ case PROC_PIDT_BSDINFOWITHUNIQID:
+ size = PROC_PIDT_BSDINFOWITHUNIQID_SIZE;
+ break;
+ case PROC_PIDARCHINFO:
+ size = PROC_PIDARCHINFO_SIZE;
+ break;
+ case PROC_PIDCOALITIONINFO:
+ size = PROC_PIDCOALITIONINFO_SIZE;
+ break;
+ case PROC_PIDNOTEEXIT:
+ /*
+ * Set findzomb explicitly because arg passed
+ * in is used as note exit status bits.
+ */
+ size = PROC_PIDNOTEEXIT_SIZE;
+ findzomb = 1;
+ break;
+ case PROC_PIDEXITREASONINFO:
+ size = PROC_PIDEXITREASONINFO_SIZE;
+ findzomb = 1;
+ break;
+ case PROC_PIDEXITREASONBASICINFO:
+ size = PROC_PIDEXITREASONBASICINFOSIZE;
+ findzomb = 1;
+ break;
+ case PROC_PIDREGIONPATHINFO2:
+ size = PROC_PIDREGIONPATHINFO2_SIZE;
+ break;
+ case PROC_PIDREGIONPATHINFO3:
+ size = PROC_PIDREGIONPATHINFO3_SIZE;
+ break;
+ case PROC_PIDLISTUPTRS:
+ size = PROC_PIDLISTUPTRS_SIZE;
+ if (buffer == USER_ADDR_NULL) {
+ size = 0;
+ }
+ break;
+ case PROC_PIDLISTDYNKQUEUES:
+ size = PROC_PIDLISTDYNKQUEUES_SIZE;
+ if (buffer == USER_ADDR_NULL) {
+ size = 0;
+ }
+ break;
+ case PROC_PIDVMRTFAULTINFO:
+ size = sizeof(vm_rtfault_record_t);
+ if (buffer == USER_ADDR_NULL) {
+ size = 0;
+ }
+ break;
+ case PROC_PIDPLATFORMINFO:
+ size = PROC_PIDPLATFORMINFO_SIZE;
+ findzomb = 1;
+ break;
+ case PROC_PIDREGIONPATH:
+ size = PROC_PIDREGIONPATH_SIZE;
+ break;
+ case PROC_PIDIPCTABLEINFO:
+ size = PROC_PIDIPCTABLEINFO_SIZE;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (buffersize < size) {
+ return ENOMEM;
+ }
+
+ if ((flavor == PROC_PIDPATHINFO) && (buffersize > PROC_PIDPATHINFO_MAXSIZE)) {
+ return EOVERFLOW;
+ }
+
+ /* Check if we need to look for zombies */
+ if ((flavor == PROC_PIDTBSDINFO) || (flavor == PROC_PIDT_SHORTBSDINFO) || (flavor == PROC_PIDT_BSDINFOWITHUNIQID)
+ || (flavor == PROC_PIDUNIQIDENTIFIERINFO)) {
+ if (arg) {
+ findzomb = 1;
+ }
+ }
+
+ if ((p = proc_find(pid)) == PROC_NULL) {
+ if (findzomb) {
+ p = proc_find_zombref(pid);
+ }
+ if (p == PROC_NULL) {
+ error = ESRCH;
+ goto out;
+ }
+ zombie = 1;
+ } else {
+ gotref = 1;
+ }
+
+ if ((flags & PIF_COMPARE_IDVERSION) && (ext_id != p->p_idversion)) {
+ error = ESRCH;
+ goto out;
+ }
+ if ((flags & PIF_COMPARE_UNIQUEID) && (ext_id != p->p_uniqueid)) {
+ error = ESRCH;
+ goto out;
+ }
+
+ /* Certain operations don't require privileges */
+ switch (flavor) {
+ case PROC_PIDT_SHORTBSDINFO:
+ case PROC_PIDUNIQIDENTIFIERINFO:
+ case PROC_PIDPATHINFO:
+ case PROC_PIDCOALITIONINFO:
+ case PROC_PIDPLATFORMINFO:
+ check_same_user = NO_CHECK_SAME_USER;
+ break;
+ default:
+ check_same_user = CHECK_SAME_USER;
+ break;
+ }
+
+ /* Do we have permission to look into this? */
+ if ((error = proc_security_policy(p, PROC_INFO_CALL_PIDINFO, flavor, check_same_user))) {
+ goto out;
+ }
+
+ switch (flavor) {
+ case PROC_PIDLISTFDS: {
+ error = proc_pidfdlist(p, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDUNIQIDENTIFIERINFO: {
+ struct proc_uniqidentifierinfo p_uniqidinfo;
+ bzero(&p_uniqidinfo, sizeof(p_uniqidinfo));
+ proc_piduniqidentifierinfo(p, &p_uniqidinfo);
+ error = copyout(&p_uniqidinfo, buffer, sizeof(struct proc_uniqidentifierinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_uniqidentifierinfo);
+ }
+ }
+ break;
+
+ case PROC_PIDT_SHORTBSDINFO:
+ shortversion = 1;
+ OS_FALLTHROUGH;
+ case PROC_PIDT_BSDINFOWITHUNIQID:
+ case PROC_PIDTBSDINFO: {
+ struct proc_bsdinfo pbsd;
+ struct proc_bsdshortinfo pbsd_short;
+ struct proc_bsdinfowithuniqid pbsd_uniqid;
+
+ if (flavor == PROC_PIDT_BSDINFOWITHUNIQID) {
+ uniqidversion = 1;
+ }
+
+ if (shortversion != 0) {
+ error = proc_pidshortbsdinfo(p, &pbsd_short, zombie);
+ } else {
+ error = proc_pidbsdinfo(p, &pbsd, zombie);
+ if (uniqidversion != 0) {
+ bzero(&pbsd_uniqid, sizeof(pbsd_uniqid));
+ proc_piduniqidentifierinfo(p, &pbsd_uniqid.p_uniqidentifier);
+ pbsd_uniqid.pbsd = pbsd;
+ }
+ }
+
+ if (error == 0) {
+ if (shortversion != 0) {
+ error = copyout(&pbsd_short, buffer, sizeof(struct proc_bsdshortinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_bsdshortinfo);
+ }
+ } else if (uniqidversion != 0) {
+ error = copyout(&pbsd_uniqid, buffer, sizeof(struct proc_bsdinfowithuniqid));
+ if (error == 0) {
+ *retval = sizeof(struct proc_bsdinfowithuniqid);
+ }
+ } else {
+ error = copyout(&pbsd, buffer, sizeof(struct proc_bsdinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_bsdinfo);
+ }
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDTASKINFO: {
+ struct proc_taskinfo ptinfo;
+
+ error = proc_pidtaskinfo(p, &ptinfo);
+ if (error == 0) {
+ error = copyout(&ptinfo, buffer, sizeof(struct proc_taskinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_taskinfo);
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDTASKALLINFO: {
+ struct proc_taskallinfo pall;
+ bzero(&pall, sizeof(pall));
+ error = proc_pidbsdinfo(p, &pall.pbsd, 0);
+ error = proc_pidtaskinfo(p, &pall.ptinfo);
+ if (error == 0) {
+ error = copyout(&pall, buffer, sizeof(struct proc_taskallinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_taskallinfo);
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDTHREADID64INFO:
+ thuniqueid = true;
+ OS_FALLTHROUGH;
+ case PROC_PIDTHREADINFO:{
+ struct proc_threadinfo pthinfo;
+
+ error = proc_pidthreadinfo(p, arg, thuniqueid, &pthinfo);
+ if (error == 0) {
+ error = copyout(&pthinfo, buffer, sizeof(struct proc_threadinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_threadinfo);
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDLISTTHREADIDS:
+ thuniqueid = true;
+ OS_FALLTHROUGH;
+ case PROC_PIDLISTTHREADS:{
+ error = proc_pidlistthreads(p, thuniqueid, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDREGIONINFO:{
+ error = proc_pidregioninfo(p, arg, buffer, buffersize, retval);
+ }
+ break;
+
+
+ case PROC_PIDREGIONPATHINFO:{
+ error = proc_pidregionpathinfo(p, arg, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDREGIONPATHINFO2:{
+ error = proc_pidregionpathinfo2(p, arg, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDREGIONPATHINFO3:{
+ error = proc_pidregionpathinfo3(p, arg, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDVNODEPATHINFO:{
+ error = proc_pidvnodepathinfo(p, arg, buffer, buffersize, retval);
+ }
+ break;
+
+
+ case PROC_PIDTHREADPATHINFO:{
+ struct proc_threadwithpathinfo pinfo;
+
+ error = proc_pidthreadpathinfo(p, arg, &pinfo);
+ if (error == 0) {
+ error = copyout((caddr_t)&pinfo, buffer, sizeof(struct proc_threadwithpathinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_threadwithpathinfo);
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDPATHINFO: {
+ error = proc_pidpathinfo(p, arg, buffer, buffersize, retval);
+ }
+ break;
+
+
+ case PROC_PIDWORKQUEUEINFO:{
+ struct proc_workqueueinfo pwqinfo;
+
+ error = proc_pidworkqueueinfo(p, &pwqinfo);
+ if (error == 0) {
+ error = copyout(&pwqinfo, buffer, sizeof(struct proc_workqueueinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_workqueueinfo);
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDLISTFILEPORTS: {
+ error = proc_pidfileportlist(p, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDARCHINFO: {
+ struct proc_archinfo pai;
+ bzero(&pai, sizeof(pai));
+ proc_archinfo(p, &pai);
+ error = copyout(&pai, buffer, sizeof(struct proc_archinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_archinfo);
+ }
+ }
+ break;
+
+ case PROC_PIDCOALITIONINFO: {
+ struct proc_pidcoalitioninfo pci;
+ proc_pidcoalitioninfo(p, &pci);
+ error = copyout(&pci, buffer, sizeof(struct proc_pidcoalitioninfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_pidcoalitioninfo);
+ }
+ }
+ break;
+
+ case PROC_PIDNOTEEXIT: {
+ uint32_t data;
+ error = proc_pidnoteexit(p, arg, &data);
+ if (error == 0) {
+ error = copyout(&data, buffer, sizeof(data));
+ if (error == 0) {
+ *retval = sizeof(data);
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDEXITREASONINFO: {
+ struct proc_exitreasoninfo eri;
+
+ error = copyin(buffer, &eri, sizeof(eri));
+ if (error != 0) {
+ break;
+ }
+
+ error = proc_pidexitreasoninfo(p, &eri, NULL);
+ if (error == 0) {
+ error = copyout(&eri, buffer, sizeof(eri));
+ if (error == 0) {
+ *retval = sizeof(eri);
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDEXITREASONBASICINFO: {
+ struct proc_exitreasonbasicinfo beri;
+
+ bzero(&beri, sizeof(struct proc_exitreasonbasicinfo));
+
+ error = proc_pidexitreasoninfo(p, NULL, &beri);
+ if (error == 0) {
+ error = copyout(&beri, buffer, sizeof(beri));
+ if (error == 0) {
+ *retval = sizeof(beri);
+ }
+ }
+ }
+ break;
+
+ case PROC_PIDLISTUPTRS:
+ error = proc_pidlistuptrs(p, buffer, buffersize, retval);
+ break;
+
+ case PROC_PIDLISTDYNKQUEUES:
+ error = kevent_copyout_proc_dynkqids(p, buffer, buffersize, retval);
+ break;
+ case PROC_PIDVMRTFAULTINFO: {
+ /* This interface can only be employed on the current
+ * process. We will eventually enforce an entitlement.
+ */
+ *retval = 0;
+
+ if (p != current_proc()) {
+ error = EINVAL;
+ break;
+ }
+
+ size_t kbufsz = MIN(buffersize, vmrtfaultinfo_bufsz());
+ void *vmrtfbuf = kheap_alloc(KHEAP_TEMP, kbufsz, Z_WAITOK | Z_ZERO);
+
+ if (vmrtfbuf == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ uint64_t effpid = get_current_unique_pid();
+ /* The VM may choose to provide more comprehensive records
+ * for root-privileged users on internal configurations.
+ */
+ boolean_t isroot = (suser(kauth_cred_get(), (u_short *)0) == 0);
+ size_t num_extracted = 0;
+ int vmf_residue = vmrtf_extract(effpid, isroot, kbufsz, vmrtfbuf, &num_extracted);
+ size_t vmfsz = num_extracted * sizeof(vm_rtfault_record_t);
+
+ *retval = (int32_t)MIN(num_extracted, INT32_MAX);
+
+ error = 0;
+ if (vmfsz) {
+ error = copyout(vmrtfbuf, buffer, vmfsz);
+ }
+
+ if (error == 0) {
+ if (vmf_residue) {
+ error = ENOMEM;
+ }
+ }
+ kheap_free(KHEAP_TEMP, vmrtfbuf, kbufsz);
+ }
+ break;
+ case PROC_PIDPLATFORMINFO: {
+ proc_lock(p);
+ uint32_t platform = p->p_platform;
+ proc_unlock(p);
+ error = copyout(&platform, buffer, sizeof(uint32_t));
+ if (error == 0) {
+ *retval = sizeof(uint32_t);
+ }
+ } break;
+ case PROC_PIDREGIONPATH: {
+ error = proc_pidregionpath(p, arg, buffer, buffersize, retval);
+ }
+ break;
+ case PROC_PIDIPCTABLEINFO: {
+ struct proc_ipctableinfo table_info;
+
+ error = proc_pidipctableinfo(p, &table_info);
+ if (error == 0) {
+ error = copyout(&table_info, buffer, sizeof(struct proc_ipctableinfo));
+ if (error == 0) {
+ *retval = sizeof(struct proc_ipctableinfo);
+ }
+ }
+ }
+ break;
+ default:
+ error = ENOTSUP;
+ break;
+ }
+
+out:
+ if (gotref) {
+ proc_rele(p);
+ } else if (zombie) {
+ proc_drop_zombref(p);
+ }
+ return error;
+}
+
+
+int
+pid_vnodeinfo(vnode_t vp, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval)
+{
+ struct vnode_fdinfo vfi;
+ uint32_t vid = vnode_vid(vp);
+ int error = 0;
+
+ if ((error = vnode_getwithvid(vp, vid)) != 0) {
+ return error;
+ }
+ bzero(&vfi, sizeof(struct vnode_fdinfo));
+ fill_fileinfo(fp, proc, fd, &vfi.pfi);
+ error = fill_vnodeinfo(vp, &vfi.pvi, FALSE);
+ vnode_put(vp);
+ if (error == 0) {
+ error = copyout((caddr_t)&vfi, buffer, sizeof(struct vnode_fdinfo));
+ if (error == 0) {
+ *retval = sizeof(struct vnode_fdinfo);
+ }
+ }
+ return error;
+}
+
+int
+pid_vnodeinfopath(vnode_t vp, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval)
+{
+ struct vnode_fdinfowithpath vfip;
+ uint32_t vid = vnode_vid(vp);
+ int count, error = 0;
+
+ if ((error = vnode_getwithvid(vp, vid)) != 0) {
+ return error;
+ }
+ bzero(&vfip, sizeof(struct vnode_fdinfowithpath));
+ fill_fileinfo(fp, proc, fd, &vfip.pfi);
+ error = fill_vnodeinfo(vp, &vfip.pvip.vip_vi, TRUE);
+ if (error == 0) {
+ count = MAXPATHLEN;
+ vn_getpath(vp, &vfip.pvip.vip_path[0], &count);
+ vfip.pvip.vip_path[MAXPATHLEN - 1] = 0;
+ vnode_put(vp);
+ error = copyout((caddr_t)&vfip, buffer, sizeof(struct vnode_fdinfowithpath));
+ if (error == 0) {
+ *retval = sizeof(struct vnode_fdinfowithpath);
+ }
+ } else {
+ vnode_put(vp);
+ }
+ return error;
+}
+
+void
+fill_fileinfo(struct fileproc * fp, proc_t proc, int fd, struct proc_fileinfo * fproc)
+{
+ fproc->fi_openflags = fp->fp_glob->fg_flag;
+ fproc->fi_status = 0;
+ fproc->fi_offset = fp->fp_glob->fg_offset;
+ fproc->fi_type = FILEGLOB_DTYPE(fp->fp_glob);
+ if (os_ref_get_count_raw(&fp->fp_glob->fg_count) > 1) {
+ fproc->fi_status |= PROC_FP_SHARED;
+ }
+ if (proc != PROC_NULL) {
+ if ((FDFLAGS_GET(proc, fd) & UF_EXCLOSE) != 0) {
+ fproc->fi_status |= PROC_FP_CLEXEC;
+ }
+ if ((FDFLAGS_GET(proc, fd) & UF_FORKCLOSE) != 0) {
+ fproc->fi_status |= PROC_FP_CLFORK;
+ }
+ }
+ if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
+ fproc->fi_status |= PROC_FP_GUARDED;
+ fproc->fi_guardflags = 0;
+ if (fp_isguarded(fp, GUARD_CLOSE)) {
+ fproc->fi_guardflags |= PROC_FI_GUARD_CLOSE;
+ }
+ if (fp_isguarded(fp, GUARD_DUP)) {
+ fproc->fi_guardflags |= PROC_FI_GUARD_DUP;
+ }
+ if (fp_isguarded(fp, GUARD_SOCKET_IPC)) {
+ fproc->fi_guardflags |= PROC_FI_GUARD_SOCKET_IPC;
+ }
+ if (fp_isguarded(fp, GUARD_FILEPORT)) {
+ fproc->fi_guardflags |= PROC_FI_GUARD_FILEPORT;
+ }
+ }
+}
+
+
+
+int
+fill_vnodeinfo(vnode_t vp, struct vnode_info *vinfo, __unused boolean_t check_fsgetpath)
+{
+ vfs_context_t context;
+ struct stat64 sb;
+ int error = 0;
+
+ bzero(&sb, sizeof(struct stat64));
+ context = vfs_context_create((vfs_context_t)0);
+#if CONFIG_MACF
+ /* Called when vnode info is used by the caller to get vnode's path */
+ if (check_fsgetpath) {
+ error = mac_vnode_check_fsgetpath(context, vp);
+ }
+#endif
+ if (!error) {
+ error = vn_stat(vp, &sb, NULL, 1, 0, context);
+ munge_vinfo_stat(&sb, &vinfo->vi_stat);
+ }
+ (void)vfs_context_rele(context);
+ if (error != 0) {
+ goto out;
+ }
+
+ if (vp->v_mount != dead_mountp) {
+ vinfo->vi_fsid = vp->v_mount->mnt_vfsstat.f_fsid;
+ } else {
+ vinfo->vi_fsid.val[0] = 0;
+ vinfo->vi_fsid.val[1] = 0;
+ }
+ vinfo->vi_type = vp->v_type;
+out:
+ return error;
+}
+
+int
+pid_socketinfo(socket_t so, struct fileproc *fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval)
+{
+#if SOCKETS
+ struct socket_fdinfo s;
+ int error = 0;
+
+ bzero(&s, sizeof(struct socket_fdinfo));
+ fill_fileinfo(fp, proc, fd, &s.pfi);
+ if ((error = fill_socketinfo(so, &s.psi)) == 0) {
+ if ((error = copyout(&s, buffer, sizeof(struct socket_fdinfo))) == 0) {
+ *retval = sizeof(struct socket_fdinfo);
+ }
+ }
+ return error;
+#else
+#pragma unused(so, fp, proc, fd, buffer)
+ *retval = 0;
+ return ENOTSUP;
+#endif
+}
+
+int
+pid_pseminfo(struct psemnode *psem, struct fileproc *fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval)
+{
+ struct psem_fdinfo pseminfo;
+ int error = 0;
+
+ bzero(&pseminfo, sizeof(struct psem_fdinfo));
+ fill_fileinfo(fp, proc, fd, &pseminfo.pfi);
+
+ if ((error = fill_pseminfo(psem, &pseminfo.pseminfo)) == 0) {
+ if ((error = copyout(&pseminfo, buffer, sizeof(struct psem_fdinfo))) == 0) {
+ *retval = sizeof(struct psem_fdinfo);
+ }
+ }
+
+ return error;
+}
+
+int
+pid_pshminfo(struct pshmnode *pshm, struct fileproc *fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval)
+{
+ struct pshm_fdinfo pshminfo;
+ int error = 0;
+
+ bzero(&pshminfo, sizeof(struct pshm_fdinfo));
+ fill_fileinfo(fp, proc, fd, &pshminfo.pfi);
+
+ if ((error = fill_pshminfo(pshm, &pshminfo.pshminfo)) == 0) {
+ if ((error = copyout(&pshminfo, buffer, sizeof(struct pshm_fdinfo))) == 0) {
+ *retval = sizeof(struct pshm_fdinfo);
+ }
+ }
+
+ return error;
+}
+
+int
+pid_pipeinfo(struct pipe * p, struct fileproc *fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval)
+{
+ struct pipe_fdinfo pipeinfo;
+ int error = 0;
+
+ bzero(&pipeinfo, sizeof(struct pipe_fdinfo));
+ fill_fileinfo(fp, proc, fd, &pipeinfo.pfi);
+ if ((error = fill_pipeinfo(p, &pipeinfo.pipeinfo)) == 0) {
+ if ((error = copyout(&pipeinfo, buffer, sizeof(struct pipe_fdinfo))) == 0) {
+ *retval = sizeof(struct pipe_fdinfo);
+ }
+ }
+
+ return error;
+}
+
+int
+pid_kqueueinfo(struct kqueue * kq, struct fileproc *fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval)
+{
+ struct kqueue_fdinfo kqinfo;
+ int error = 0;
+
+ bzero(&kqinfo, sizeof(struct kqueue_fdinfo));
+
+ /* not all kq's are associated with a file (e.g. workqkq) */
+ if (fp) {
+ assert(fd >= 0);
+ fill_fileinfo(fp, proc, fd, &kqinfo.pfi);
+ }
+
+ if ((error = fill_kqueueinfo(kq, &kqinfo.kqueueinfo)) == 0) {
+ if ((error = copyout(&kqinfo, buffer, sizeof(struct kqueue_fdinfo))) == 0) {
+ *retval = sizeof(struct kqueue_fdinfo);
+ }
+ }
+
+ return error;
+}
+
+
+/************************** proc_pidfdinfo routine ***************************/
+int
+proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval)
+{
+ proc_t p;
+ int error = ENOTSUP;
+ struct fileproc *fp = NULL;
+ uint32_t size;
+
+ switch (flavor) {
+ case PROC_PIDFDVNODEINFO:
+ size = PROC_PIDFDVNODEINFO_SIZE;
+ break;
+ case PROC_PIDFDVNODEPATHINFO:
+ size = PROC_PIDFDVNODEPATHINFO_SIZE;
+ break;
+ case PROC_PIDFDSOCKETINFO:
+ size = PROC_PIDFDSOCKETINFO_SIZE;
+ break;
+ case PROC_PIDFDPSEMINFO:
+ size = PROC_PIDFDPSEMINFO_SIZE;
+ break;
+ case PROC_PIDFDPSHMINFO:
+ size = PROC_PIDFDPSHMINFO_SIZE;
+ break;
+ case PROC_PIDFDPIPEINFO:
+ size = PROC_PIDFDPIPEINFO_SIZE;
+ break;
+ case PROC_PIDFDKQUEUEINFO:
+ size = PROC_PIDFDKQUEUEINFO_SIZE;
+ break;
+ case PROC_PIDFDKQUEUE_EXTINFO:
+ size = PROC_PIDFDKQUEUE_EXTINFO_SIZE;
+ if (buffer == (user_addr_t)0) {
+ size = 0;
+ }
+ break;
+ case PROC_PIDFDATALKINFO:
+ size = PROC_PIDFDATALKINFO_SIZE;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ if (buffersize < size) {
+ return ENOMEM;
+ }
+
+ if ((p = proc_find(pid)) == PROC_NULL) {
+ error = ESRCH;
+ goto out;
+ }
+
+ /* Do we have permission to look into this? */
+ if ((error = proc_security_policy(p, PROC_INFO_CALL_PIDFDINFO, flavor, CHECK_SAME_USER))) {
+ goto out1;
+ }
+
+ switch (flavor) {
+ case PROC_PIDFDVNODEINFO: {
+ if ((error = fp_get_ftype(p, fd, DTYPE_VNODE, EBADF, &fp)) != 0) {
+ goto out1;
+ }
+ error = pid_vnodeinfo(fp->fp_glob->fg_data, fp, p, fd, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDFDVNODEPATHINFO: {
+ if ((error = fp_get_ftype(p, fd, DTYPE_VNODE, EBADF, &fp)) != 0) {
+ goto out1;
+ }
+ error = pid_vnodeinfopath(fp->fp_glob->fg_data, fp, p, fd, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDFDSOCKETINFO: {
+ if ((error = fp_get_ftype(p, fd, DTYPE_SOCKET, ENOTSOCK, &fp)) != 0) {
+ goto out1;
+ }
+ error = pid_socketinfo(fp->fp_glob->fg_data, fp, p, fd, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDFDPSEMINFO: {
+ if ((error = fp_get_ftype(p, fd, DTYPE_PSXSHM, EBADF, &fp)) != 0) {
+ goto out1;
+ }
+ error = pid_pseminfo(fp->fp_glob->fg_data, fp, p, fd, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDFDPSHMINFO: {
+ if ((error = fp_get_ftype(p, fd, DTYPE_PSXSHM, EBADF, &fp)) != 0) {
+ goto out1;
+ }
+ error = pid_pshminfo(fp->fp_glob->fg_data, fp, p, fd, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDFDPIPEINFO: {
+ if ((error = fp_get_ftype(p, fd, DTYPE_PIPE, EBADF, &fp)) != 0) {
+ goto out1;
+ }
+ error = pid_pipeinfo(fp->fp_glob->fg_data, fp, p, fd, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDFDKQUEUEINFO: {
+ kqueue_t kqu;
+
+ if (fd == -1) {
+ if ((kqu.kqwq = p->p_fd->fd_wqkqueue) == NULL) {
+ /* wqkqueue is initialized on-demand */
+ error = 0;
+ break;
+ }
+ } else if ((error = fp_get_ftype(p, fd, DTYPE_KQUEUE, EBADF, &fp)) != 0) {
+ goto out1;
+ } else {
+ kqu.kq = fp->fp_glob->fg_data;
+ }
+
+ error = pid_kqueueinfo(kqu.kq, fp, p, fd, buffer, buffersize, retval);
+ }
+ break;
+
+ case PROC_PIDFDKQUEUE_EXTINFO: {
+ kqueue_t kqu;
+
+ if (fd == -1) {
+ if ((kqu.kqwq = p->p_fd->fd_wqkqueue) == NULL) {
+ /* wqkqueue is initialized on-demand */
+ error = 0;
+ break;
+ }
+ } else if ((error = fp_get_ftype(p, fd, DTYPE_KQUEUE, EBADF, &fp)) != 0) {
+ goto out1;
+ } else {
+ kqu.kq = fp->fp_glob->fg_data;
+ }
+ error = pid_kqueue_extinfo(p, kqu.kq, buffer, buffersize, retval);
+ }
+ break;
+
+ default: {
+ error = EINVAL;
+ goto out1;
+ }
+ }
+
+ if (fp) {
+ fp_drop(p, fd, fp, 0);
+ }
+out1:
+ proc_rele(p);
+out:
+ return error;
+}
+
+#define MAX_UPTRS 16392
+
+int
+proc_pidlistuptrs(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval)
+{
+ uint32_t count = 0;
+ int error = 0;
+ void *kbuf = NULL;
+ int32_t nuptrs = 0;
+
+ if (buffer == USER_ADDR_NULL || buffersize < sizeof(uint64_t)) {
+ buffersize = 0;
+ } else {
+ count = MIN(buffersize / sizeof(uint64_t), MAX_UPTRS);
+ buffersize = count * sizeof(uint64_t);
+ kbuf = kheap_alloc(KHEAP_TEMP, buffersize, Z_WAITOK);
+ }
+
+ nuptrs = kevent_proc_copy_uptrs(p, kbuf, buffersize);
+
+ if (kbuf) {
+ size_t copysize;
+ if (os_mul_overflow(nuptrs, sizeof(uint64_t), ©size)) {
+ error = ERANGE;
+ goto out;
+ }
+ if (copysize > buffersize) {
+ copysize = buffersize;
+ }
+ error = copyout(kbuf, buffer, copysize);
+ }
+
+out:
+ *retval = nuptrs;
+
+ if (kbuf) {
+ kheap_free(KHEAP_TEMP, kbuf, buffersize);
+ kbuf = NULL;
+ }
+
+ return error;
+}
+
+/*
+ * Helper function for proc_pidfileportinfo
+ */
+
+struct fileport_info_args {
+ int fia_flavor;
+ user_addr_t fia_buffer;
+ uint32_t fia_buffersize;
+ int32_t *fia_retval;
+};
+
+static kern_return_t
+proc_fileport_info(__unused mach_port_name_t name,
+ struct fileglob *fg, void *arg)
+{
+ struct fileport_info_args *fia = arg;
+ struct fileproc __fileproc, *fp = &__fileproc;
+ int error;
+
+ bzero(fp, sizeof(*fp));
+ fp->fp_glob = fg;
+
+ switch (fia->fia_flavor) {
+ case PROC_PIDFILEPORTVNODEPATHINFO: {
+ vnode_t vp;
+
+ if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
+ error = ENOTSUP;
+ break;
+ }
+ vp = (struct vnode *)fg->fg_data;
+ error = pid_vnodeinfopath(vp, fp, PROC_NULL, 0,
+ fia->fia_buffer, fia->fia_buffersize, fia->fia_retval);
+ } break;
+
+ case PROC_PIDFILEPORTSOCKETINFO: {
+ socket_t so;
+
+ if (FILEGLOB_DTYPE(fg) != DTYPE_SOCKET) {
+ error = EOPNOTSUPP;
+ break;
+ }
+ so = (socket_t)fg->fg_data;
+ error = pid_socketinfo(so, fp, PROC_NULL, 0,
+ fia->fia_buffer, fia->fia_buffersize, fia->fia_retval);
+ } break;
+
+ case PROC_PIDFILEPORTPSHMINFO: {
+ struct pshmnode *pshm;
+
+ if (FILEGLOB_DTYPE(fg) != DTYPE_PSXSHM) {
+ error = EBADF; /* ick - mirror fp_getfpshm */
+ break;
+ }
+ pshm = (struct pshmnode *)fg->fg_data;
+ error = pid_pshminfo(pshm, fp, PROC_NULL, 0,
+ fia->fia_buffer, fia->fia_buffersize, fia->fia_retval);
+ } break;
+
+ case PROC_PIDFILEPORTPIPEINFO: {
+ struct pipe *cpipe;
+
+ if (FILEGLOB_DTYPE(fg) != DTYPE_PIPE) {
+ error = EBADF; /* ick - mirror fp_getfpipe */
+ break;
+ }
+ cpipe = (struct pipe *)fg->fg_data;
+ error = pid_pipeinfo(cpipe, fp, PROC_NULL, 0,
+ fia->fia_buffer, fia->fia_buffersize, fia->fia_retval);
+ } break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/************************* proc_pidfileportinfo routine *********************/
+int
+proc_pidfileportinfo(int pid, int flavor, mach_port_name_t name,
+ user_addr_t buffer, uint32_t buffersize, int32_t *retval)
+{
+ proc_t p;
+ int error = ENOTSUP;
+ uint32_t size;
+ struct fileport_info_args fia;
+
+ /* fileport types are restricted by file_issendable() */
+
+ switch (flavor) {
+ case PROC_PIDFILEPORTVNODEPATHINFO:
+ size = PROC_PIDFILEPORTVNODEPATHINFO_SIZE;
+ break;
+ case PROC_PIDFILEPORTSOCKETINFO:
+ size = PROC_PIDFILEPORTSOCKETINFO_SIZE;
+ break;
+ case PROC_PIDFILEPORTPSHMINFO:
+ size = PROC_PIDFILEPORTPSHMINFO_SIZE;
+ break;
+ case PROC_PIDFILEPORTPIPEINFO:
+ size = PROC_PIDFILEPORTPIPEINFO_SIZE;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (buffersize < size) {
+ return ENOMEM;
+ }
+ if ((p = proc_find(pid)) == PROC_NULL) {
+ error = ESRCH;
+ goto out;
+ }
+
+ /* Do we have permission to look into this? */
+ if ((error = proc_security_policy(p, PROC_INFO_CALL_PIDFILEPORTINFO, flavor, CHECK_SAME_USER))) {
+ goto out1;
+ }
+
+ fia.fia_flavor = flavor;
+ fia.fia_buffer = buffer;
+ fia.fia_buffersize = buffersize;
+ fia.fia_retval = retval;
+
+ if (fileport_invoke(p->task, name,
+ proc_fileport_info, &fia, &error) != KERN_SUCCESS) {
+ error = EINVAL;
+ }
+out1:
+ proc_rele(p);
+out:
+ return error;
+}
+
+int
+proc_security_policy(proc_t targetp, __unused int callnum, __unused int flavor, boolean_t check_same_user)
+{
+#if CONFIG_MACF
+ int error = 0;
+
+ if ((error = mac_proc_check_proc_info(current_proc(), targetp, callnum, flavor))) {
+ return error;
+ }
+#endif
+
+ /* The 'listpids' call doesn't have a target proc */
+ if (targetp == PROC_NULL) {
+ assert(callnum == PROC_INFO_CALL_LISTPIDS && check_same_user == NO_CHECK_SAME_USER);
+ return 0;
+ }
+
+ /*
+ * Check for 'get information for processes owned by other users' privilege
+ * root has this privilege by default
+ */
+ if (priv_check_cred(kauth_cred_get(), PRIV_GLOBAL_PROC_INFO, 0) == 0) {
+ check_same_user = FALSE;
+ }
+
+ if (check_same_user) {
+ kauth_cred_t target_cred;
+ uid_t target_uid;
+
+ target_cred = kauth_cred_proc_ref(targetp);
+ target_uid = kauth_cred_getuid(target_cred);
+ kauth_cred_unref(&target_cred);
+
+ if (kauth_getuid() != target_uid) {
+ return EPERM;
+ }
+ }
+
+ return 0;
+}
+
+int
+proc_kernmsgbuf(user_addr_t buffer, uint32_t buffersize, int32_t * retval)
+{
+#if CONFIG_MACF
+ int error = 0;
+
+ if ((error = mac_system_check_info(kauth_cred_get(), "kern.msgbuf"))) {
+ return error;
+ }
+#endif
+
+ if (suser(kauth_cred_get(), (u_short *)0) == 0) {
+ return log_dmesg(buffer, buffersize, retval);
+ } else {
+ return EPERM;
+ }
+}
+
+/* ********* process control sets on self only */
+int
+proc_setcontrol(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, __unused int32_t * retval)
+{
+ struct proc * pself = PROC_NULL;
+ int error = 0;
+ uint32_t pcontrol = (uint32_t)arg;
+ struct uthread *ut = NULL;
+ char name_buf[MAXTHREADNAMESIZE];