X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/316670eb35587141e969394ae8537d66b9211e80..4bd07ac2140668789aa3ee8ec4dde4a3e0a3bba5:/bsd/kern/proc_info.c?ds=sidebyside diff --git a/bsd/kern/proc_info.c b/bsd/kern/proc_info.c index 482f83e0e..e26393c52 100644 --- a/bsd/kern/proc_info.c +++ b/bsd/kern/proc_info.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2013 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -56,7 +56,6 @@ #include #include #include -#include #include #include #include @@ -72,8 +71,9 @@ #include #include #include +#include -#include +#include #include @@ -81,6 +81,15 @@ #include +/* Needed by proc_pidnoteexit() */ +#include +#include + +/* Needed by proc_listcoalitions() */ +#ifdef CONFIG_COALITIONS +#include +#endif + struct pshmnode; struct psemnode; struct pipe; @@ -88,57 +97,97 @@ struct kqueue; struct atalk; uint64_t get_dispatchqueue_offset_from_proc(void *); +uint64_t get_dispatchqueue_serialno_offset_from_proc(void *); int proc_info_internal(int callnum, int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +/* + * TODO: Replace the noinline attribute below. Currently, it serves + * to avoid stack bloat caused by inlining multiple functions that + * have large stack footprints; when the functions are independent + * of each other (will not both be called in any given call to the + * caller), this only serves to bloat the stack, as we allocate + * space for both functions, despite the fact that we only need a + * fraction of that space. + * + * Long term, these functions should not be allocating everything on + * the stack, and should move large allocations (the huge structs + * that proc info deals in) to the heap, or eliminate them if + * possible. + * + * The functions that most desperately need to improve stack usage + * (starting with the worst offenders): + * proc_pidvnodepathinfo + * proc_pidinfo + * proc_pidregionpathinfo + * pid_vnodeinfopath + * pid_pshminfo + * pid_pseminfo + * pid_socketinfo + * proc_pid_rusage + * proc_pidoriginatorinfo + */ + /* protos for proc_info calls */ -int proc_listpids(uint32_t type, uint32_t tyoneinfo, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int proc_pidinfo(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int proc_pidfdinfo(int pid, int flavor,int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int proc_kernmsgbuf(user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int proc_setcontrol(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int proc_pidfileportinfo(int pid, int flavor, mach_port_name_t name, user_addr_t buffer, uint32_t buffersize, int32_t *retval); -int proc_dirtycontrol(int pid, int flavor, uint64_t arg, int32_t * retval); -int proc_terminate(int pid, int32_t * retval); +int __attribute__ ((noinline)) proc_listpids(uint32_t type, uint32_t tyoneinfo, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) proc_pidinfo(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) proc_pidfdinfo(int pid, int flavor,int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) proc_kernmsgbuf(user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) proc_setcontrol(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) proc_pidfileportinfo(int pid, int flavor, mach_port_name_t name, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_dirtycontrol(int pid, int flavor, uint64_t arg, int32_t * retval); +int __attribute__ ((noinline)) proc_terminate(int pid, int32_t * retval); +int __attribute__ ((noinline)) proc_pid_rusage(int pid, int flavor, user_addr_t buffer, int32_t * retval); +int __attribute__ ((noinline)) proc_pidoriginatorinfo(int pid, int flavor, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) proc_listcoalitions(int flavor, int coaltype, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_can_use_foreground_hw(int pid, user_addr_t reason, uint32_t resonsize, int32_t *retval); /* protos for procpidinfo calls */ -int proc_pidfdlist(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval); -int proc_pidbsdinfo(proc_t p, struct proc_bsdinfo *pbsd, int zombie); -int proc_pidshortbsdinfo(proc_t p, struct proc_bsdshortinfo *pbsd_shortp, int zombie); -int proc_pidtaskinfo(proc_t p, struct proc_taskinfo *ptinfo); -int proc_pidallinfo(proc_t p, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); -int proc_pidthreadinfo(proc_t p, uint64_t arg, int thuniqueid, struct proc_threadinfo *pthinfo); -int proc_pidthreadpathinfo(proc_t p, uint64_t arg, struct proc_threadwithpathinfo *pinfo); -int proc_pidlistthreads(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval); -int proc_pidregioninfo(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); -int proc_pidregionpathinfo(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); -int proc_pidvnodepathinfo(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); -int proc_pidpathinfo(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); -int proc_pidworkqueueinfo(proc_t p, struct proc_workqueueinfo *pwqinfo); -int proc_pidfileportlist(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidfdlist(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidbsdinfo(proc_t p, struct proc_bsdinfo *pbsd, int zombie); +int __attribute__ ((noinline)) proc_pidshortbsdinfo(proc_t p, struct proc_bsdshortinfo *pbsd_shortp, int zombie); +int __attribute__ ((noinline)) proc_pidtaskinfo(proc_t p, struct proc_taskinfo *ptinfo); +int __attribute__ ((noinline)) proc_pidthreadinfo(proc_t p, uint64_t arg, int thuniqueid, struct proc_threadinfo *pthinfo); +int __attribute__ ((noinline)) proc_pidthreadpathinfo(proc_t p, uint64_t arg, struct proc_threadwithpathinfo *pinfo); +int __attribute__ ((noinline)) proc_pidlistthreads(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidregioninfo(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidregionpathinfo(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidregionpathinfo2(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidregionpathinfo3(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidvnodepathinfo(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidpathinfo(proc_t p, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +int __attribute__ ((noinline)) proc_pidworkqueueinfo(proc_t p, struct proc_workqueueinfo *pwqinfo); +int __attribute__ ((noinline)) proc_pidfileportlist(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval); +void __attribute__ ((noinline)) proc_piduniqidentifierinfo(proc_t p, struct proc_uniqidentifierinfo *p_uniqidinfo); +void __attribute__ ((noinline)) proc_archinfo(proc_t p, struct proc_archinfo *pai); +void __attribute__ ((noinline)) proc_pidcoalitioninfo(proc_t p, struct proc_pidcoalitioninfo *pci); +int __attribute__ ((noinline)) proc_pidnoteexit(proc_t p, uint64_t arg, uint32_t *data); +int __attribute__ ((noinline)) proc_pidoriginatorpid_uuid(uuid_t uuid, uint32_t buffersize, pid_t *pid); /* protos for proc_pidfdinfo calls */ -int pid_vnodeinfo(vnode_t vp, uint32_t vid, struct fileproc * fp, int closeonexec, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int pid_vnodeinfopath(vnode_t vp, uint32_t vid, struct fileproc * fp, int closeonexec, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int pid_socketinfo(socket_t so, struct fileproc *fp, int closeonexec, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int pid_pseminfo(struct psemnode * psem, struct fileproc * fp, int closeonexec, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int pid_pshminfo(struct pshmnode * pshm, struct fileproc * fp, int closeonexec, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int pid_pipeinfo(struct pipe * p, struct fileproc * fp, int closeonexec, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int pid_kqueueinfo(struct kqueue * kq, struct fileproc * fp, int closeonexec, user_addr_t buffer, uint32_t buffersize, int32_t * retval); -int pid_atalkinfo(struct atalk * at, struct fileproc * fp, int closeonexec, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) pid_vnodeinfo(vnode_t vp, uint32_t vid, struct fileproc * fp,proc_t proc, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) pid_vnodeinfopath(vnode_t vp, uint32_t vid, struct fileproc * fp,proc_t proc, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) pid_socketinfo(socket_t so, struct fileproc *fp,proc_t proc, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) pid_pseminfo(struct psemnode * psem, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) pid_pshminfo(struct pshmnode * pshm, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) pid_pipeinfo(struct pipe * p, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) pid_kqueueinfo(struct kqueue * kq, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); +int __attribute__ ((noinline)) pid_atalkinfo(struct atalk * at, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, uint32_t buffersize, int32_t * retval); /* protos for misc */ -void proc_dirty_start(struct proc *p); -void proc_dirty_end(struct proc *p); - int fill_vnodeinfo(vnode_t vp, struct vnode_info *vinfo); -void fill_fileinfo(struct fileproc * fp, int closeonexec, struct proc_fileinfo * finfo); -static int proc_security_policy(proc_t p); +void fill_fileinfo(struct fileproc * fp, proc_t proc, int fd, struct proc_fileinfo * finfo); +int proc_security_policy(proc_t targetp, int callnum, int flavor, boolean_t check_same_user); static void munge_vinfo_stat(struct stat64 *sbp, struct vinfo_stat *vsbp); +static int proc_piduuidinfo(pid_t pid, uuid_t uuid_buf, uint32_t buffersize); extern int cansignal(struct proc *, kauth_cred_t, struct proc *, int, int); +extern int proc_get_rusage(proc_t proc, int flavor, user_addr_t buffer, int is_zombie); + +#define CHECK_SAME_USER TRUE +#define NO_CHECK_SAME_USER FALSE uint64_t get_dispatchqueue_offset_from_proc(void *p) { @@ -150,6 +199,16 @@ uint64_t get_dispatchqueue_offset_from_proc(void *p) } } +uint64_t get_dispatchqueue_serialno_offset_from_proc(void *p) +{ + if(p != NULL) { + proc_t pself = (proc_t)p; + return (pself->p_dispatchqueue_serialno_offset); + } else { + return (uint64_t)0; + } +} + /***************************** proc_info ********************/ int @@ -164,23 +223,32 @@ proc_info_internal(int callnum, int pid, int flavor, uint64_t arg, user_addr_t b { switch(callnum) { - case 1: /* proc_listpids */ + case PROC_INFO_CALL_LISTPIDS: /* pid contains type and flavor contains typeinfo */ return(proc_listpids(pid, flavor, buffer, buffersize, retval)); - case 2: /* proc_pidinfo */ + case PROC_INFO_CALL_PIDINFO: return(proc_pidinfo(pid, flavor, arg, buffer, buffersize, retval)); - case 3: /* proc_pidfdinfo */ + case PROC_INFO_CALL_PIDFDINFO: return(proc_pidfdinfo(pid, flavor, (int)arg, buffer, buffersize, retval)); - case 4: /* proc_kernmsgbuf */ + case PROC_INFO_CALL_KERNMSGBUF: return(proc_kernmsgbuf(buffer, buffersize, retval)); - case 5: /* set on self properties proc_setcontrol */ + case PROC_INFO_CALL_SETCONTROL: return(proc_setcontrol(pid, flavor, arg, buffer, buffersize, retval)); - case 6: /* proc_pidfileportinfo */ + case PROC_INFO_CALL_PIDFILEPORTINFO: return(proc_pidfileportinfo(pid, flavor, (mach_port_name_t)arg, buffer, buffersize, retval)); - case 7: /* proc_terminate */ + case PROC_INFO_CALL_TERMINATE: return(proc_terminate(pid, retval)); - case 8: /* proc_dirtycontrol */ + case PROC_INFO_CALL_DIRTYCONTROL: return(proc_dirtycontrol(pid, flavor, arg, retval)); + case PROC_INFO_CALL_PIDRUSAGE: + return (proc_pid_rusage(pid, flavor, buffer, retval)); + case PROC_INFO_CALL_PIDORIGINATORINFO: + return (proc_pidoriginatorinfo(pid, flavor, buffer, buffersize, retval)); + case PROC_INFO_CALL_LISTCOALITIONS: + return proc_listcoalitions(pid /* flavor */, flavor /* coaltype */, buffer, + buffersize, retval); + case PROC_INFO_CALL_CANUSEFGHW: + return proc_can_use_foreground_hw(pid, buffer, buffersize, retval); default: return(EINVAL); } @@ -201,6 +269,10 @@ proc_listpids(uint32_t type, uint32_t typeinfo, user_addr_t buffer, uint32_t bu int error = 0; struct proclist *current_list; + /* Do we have permission to look into this? */ + if ((error = proc_security_policy(PROC_NULL, PROC_INFO_CALL_LISTPIDS, type, NO_CHECK_SAME_USER))) + return (error); + /* if the buffer is null, return num of procs */ if (buffer == (user_addr_t)0) { *retval = ((nprocs+20) * sizeof(int)); @@ -308,7 +380,7 @@ proc_loop: } -/********************************** proc_pidinfo routines ********************************/ +/********************************** proc_pidfdlist routines ********************************/ int proc_pidfdlist(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval) @@ -347,8 +419,10 @@ proc_pidfdlist(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retv for (n = 0; ((n < numfds) && (n < p->p_fd->fd_nfiles)); n++) { if (((fp = p->p_fd->fd_ofiles[n]) != 0) && ((p->p_fd->fd_ofileflags[n] & UF_RESERVED) == 0)) { + file_type_t fdtype = FILEGLOB_DTYPE(fp->f_fglob); pfd->proc_fd = n; - pfd->proc_fdtype = fp->f_fglob->fg_type; + pfd->proc_fdtype = (fdtype != DTYPE_ATALK) ? + fdtype : PROX_FDTYPE_ATALK; count++; pfd++; } @@ -386,7 +460,10 @@ proc_fileport_fdtype(mach_port_name_t name, struct fileglob *fg, void *arg) struct fileport_fdtype_args *ffa = arg; if (ffa->ffa_pfi != ffa->ffa_pfi_end) { - ffa->ffa_pfi->proc_fdtype = fg->fg_type; + file_type_t fdtype = FILEGLOB_DTYPE(fg); + + ffa->ffa_pfi->proc_fdtype = (fdtype != DTYPE_ATALK) ? + fdtype : PROX_FDTYPE_ATALK; ffa->ffa_pfi->proc_fileport = name; ffa->ffa_pfi++; return (0); /* keep walking */ @@ -536,10 +613,8 @@ proc_pidbsdinfo(proc_t p, struct proc_bsdinfo * pbsd, int zombie) pbsd->pbi_flags |= PROC_FLAG_CTTY; } -#if !CONFIG_EMBEDDED if ((p->p_flag & P_DELAYIDLESLEEP) == P_DELAYIDLESLEEP) pbsd->pbi_flags |= PROC_FLAG_DELAYIDLESLEEP; -#endif /* !CONFIG_EMBEDDED */ switch(PROC_CONTROL_STATE(p)) { case P_PCTHROTTLE: @@ -617,10 +692,8 @@ proc_pidshortbsdinfo(proc_t p, struct proc_bsdshortinfo * pbsd_shortp, int zombi pbsd_shortp->pbsi_flags |= PROC_FLAG_PSUGID; if ((p->p_flag & P_EXEC) == P_EXEC) pbsd_shortp->pbsi_flags |= PROC_FLAG_EXEC; -#if !CONFIG_EMBEDDED if ((p->p_flag & P_DELAYIDLESLEEP) == P_DELAYIDLESLEEP) pbsd_shortp->pbsi_flags |= PROC_FLAG_DELAYIDLESLEEP; -#endif /* !CONFIG_EMBEDDED */ switch(PROC_CONTROL_STATE(p)) { case P_PCTHROTTLE: @@ -827,6 +900,105 @@ proc_pidregionpathinfo(proc_t p, uint64_t arg, user_addr_t buffer, __unused uint return(error); } +int +proc_pidregionpathinfo2(proc_t p, uint64_t arg, user_addr_t buffer, __unused uint32_t buffersize, int32_t *retval) +{ + struct proc_regionwithpathinfo preginfo; + int ret, error = 0; + uintptr_t vnodeaddr= 0; + uint32_t vnodeid= 0; + vnode_t vp; + int count; + + bzero(&preginfo, sizeof(struct proc_regionwithpathinfo)); + + ret = fill_procregioninfo_onlymappedvnodes( p->task, arg, (struct proc_regioninfo_internal *)&preginfo.prp_prinfo, (uintptr_t *)&vnodeaddr, (uint32_t *)&vnodeid); + if (ret == 0) + return(EINVAL); + if (!vnodeaddr) + return(EINVAL); + + vp = (vnode_t)vnodeaddr; + if ((vnode_getwithvid(vp, vnodeid)) == 0) { + /* FILL THE VNODEINFO */ + error = fill_vnodeinfo(vp, &preginfo.prp_vip.vip_vi); + count = MAXPATHLEN; + vn_getpath(vp, &preginfo.prp_vip.vip_path[0], &count); + /* Always make sure it is null terminated */ + preginfo.prp_vip.vip_path[MAXPATHLEN-1] = 0; + vnode_put(vp); + } else { + return(EINVAL); + } + + error = copyout(&preginfo, buffer, sizeof(struct proc_regionwithpathinfo)); + if (error == 0) + *retval = sizeof(struct proc_regionwithpathinfo); + return(error); +} + +int +proc_pidregionpathinfo3(proc_t p, uint64_t arg, user_addr_t buffer, __unused uint32_t buffersize, int32_t *retval) +{ + struct proc_regionwithpathinfo preginfo; + int ret, error = 0; + uintptr_t vnodeaddr; + uint32_t vnodeid; + vnode_t vp; + int count; + uint64_t addr = 0; + + /* Loop while looking for vnodes that match dev_t filter */ + do { + bzero(&preginfo, sizeof(struct proc_regionwithpathinfo)); + vnodeaddr = 0; + vnodeid = 0; + + ret = fill_procregioninfo_onlymappedvnodes( p->task, addr, (struct proc_regioninfo_internal *)&preginfo.prp_prinfo, (uintptr_t *)&vnodeaddr, (uint32_t *)&vnodeid); + if (ret == 0) + return(EINVAL); + if (!vnodeaddr) + return(EINVAL); + + vp = (vnode_t)vnodeaddr; + if ((vnode_getwithvid(vp, vnodeid)) == 0) { + /* Check if the vnode matches the filter, otherwise loop looking for the next memory region backed by a vnode */ + struct vnode_attr va; + + memset(&va, 0, sizeof(va)); + VATTR_INIT(&va); + VATTR_WANTED(&va, va_fsid); + + ret = vnode_getattr(vp, &va, vfs_context_current()); + if (ret) { + vnode_put(vp); + return(EINVAL); + } + + if (va.va_fsid == arg) { + /* FILL THE VNODEINFO */ + error = fill_vnodeinfo(vp, &preginfo.prp_vip.vip_vi); + count = MAXPATHLEN; + vn_getpath(vp, &preginfo.prp_vip.vip_path[0], &count); + /* Always make sure it is null terminated */ + preginfo.prp_vip.vip_path[MAXPATHLEN-1] = 0; + vnode_put(vp); + break; + } + vnode_put(vp); + } else { + return(EINVAL); + } + + addr = preginfo.prp_prinfo.pri_address + preginfo.prp_prinfo.pri_size; + } while (1); + + error = copyout(&preginfo, buffer, sizeof(struct proc_regionwithpathinfo)); + if (error == 0) + *retval = sizeof(struct proc_regionwithpathinfo); + return(error); +} + /* * Path is relative to current process directory; may different from current * thread directory. @@ -945,74 +1117,105 @@ proc_pidworkqueueinfo(proc_t p, struct proc_workqueueinfo *pwqinfo) return(0); } -/********************************** proc_pidinfo ********************************/ -int -proc_pidinfo(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, int32_t * retval) +void +proc_piduniqidentifierinfo(proc_t p, struct proc_uniqidentifierinfo *p_uniqidinfo) +{ + p_uniqidinfo->p_uniqueid = proc_uniqueid(p); + proc_getexecutableuuid(p, (unsigned char *)&p_uniqidinfo->p_uuid, sizeof(p_uniqidinfo->p_uuid)); + p_uniqidinfo->p_puniqueid = proc_puniqueid(p); + p_uniqidinfo->p_reserve2 = 0; + p_uniqidinfo->p_reserve3 = 0; + p_uniqidinfo->p_reserve4 = 0; +} + + +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)); +} + +/***************************** proc_pidoriginatorinfo ***************************/ + +int +proc_pidoriginatorinfo(int pid, int flavor, user_addr_t buffer, uint32_t buffersize, int32_t * retval) +{ int error = ENOTSUP; - int gotref = 0; - int findzomb = 0; - int refheld = 0, shortversion = 0; uint32_t size; - int zombie = 0; - int thuniqueid = 0; switch (flavor) { - case PROC_PIDLISTFDS: - size = PROC_PIDLISTFD_SIZE; - if (buffer == (user_addr_t)0) - 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_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; + case PROC_PIDORIGINATOR_UUID: + size = PROC_PIDORIGINATOR_UUID_SIZE; 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; + case PROC_PIDORIGINATOR_BGSTATE: + size = PROC_PIDORIGINATOR_BGSTATE_SIZE; break; - case PROC_PIDTHREADID64INFO: - size = PROC_PIDTHREADID64INFO_SIZE; + case PROC_PIDORIGINATOR_PID_UUID: + size = PROC_PIDORIGINATOR_PID_UUID_SIZE; break; default: return(EINVAL); @@ -1021,126 +1224,569 @@ proc_pidinfo(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t bu if (buffersize < size) return(ENOMEM); - if ((flavor == PROC_PIDPATHINFO) && (buffersize > PROC_PIDPATHINFO_MAXSIZE)) { - return(EOVERFLOW); - } + if (pid != 0 && pid != proc_selfpid()) + return (EINVAL); - if ((flavor != PROC_PIDTBSDINFO) && (flavor != PROC_PIDPATHINFO) && (flavor != PROC_PIDT_SHORTBSDINFO)) { - if ((p = proc_find(pid)) == PROC_NULL) { - error = ESRCH; + switch (flavor) { + case PROC_PIDORIGINATOR_UUID: { + uuid_t uuid; + + error = proc_pidoriginatoruuid(uuid, sizeof(uuid)); + if (error != 0) goto out; - } else { - gotref = 1; - /* Do we have permission to look into this ? */ - if ((error = proc_security_policy(p)) != 0) { - goto out; - } - } - } - switch (flavor) { - case PROC_PIDLISTFDS: { - error = proc_pidfdlist(p, buffer, buffersize, retval); + error = copyout(uuid, buffer, size); + if (error == 0) + *retval = size; } break; - case PROC_PIDT_SHORTBSDINFO: - shortversion = 1; - case PROC_PIDTBSDINFO: { - struct proc_bsdinfo pbsd; - struct proc_bsdshortinfo pbsd_short; + case PROC_PIDORIGINATOR_PID_UUID: { + struct proc_originatorinfo originator_info; - zombie = 0; - if (arg) - findzomb = 1; - p = proc_find(pid); - if (p == PROC_NULL) { - if (findzomb) - p = proc_find_zombref(pid); - if (p == NULL) { - error = ESRCH; - goto out; - } - zombie = 1; - } - refheld = 1; - /* Do we have permission to look into this ? */ - if ((flavor != PROC_PIDT_SHORTBSDINFO) && ((error = proc_security_policy(p)) != 0)) { - if (refheld != 0) { - if (zombie != 0) - proc_drop_zombref(p); - else - proc_rele(p); - } + error = proc_pidoriginatorpid_uuid(originator_info.originator_uuid, + sizeof(uuid_t), &originator_info.originator_pid); + if (error != 0) goto out; - } - if (shortversion != 0) { - error = proc_pidshortbsdinfo(p, &pbsd_short, zombie); - } else { - error = proc_pidbsdinfo(p, &pbsd, zombie); - } - if (refheld != 0) { - if (zombie != 0) - proc_drop_zombref(p); - else - proc_rele(p); - } - if (error == 0) { - if (shortversion != 0) { - error = copyout(&pbsd_short, buffer, sizeof(struct proc_bsdshortinfo)); - if (error == 0) - *retval = sizeof(struct proc_bsdshortinfo); - } 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; + originator_info.p_reserve2 = 0; + originator_info.p_reserve3 = 0; + originator_info.p_reserve4 = 0; - error = proc_pidtaskinfo(p, &ptinfo); - if (error == 0) { - error = copyout(&ptinfo, buffer, sizeof(struct proc_taskinfo)); - if (error == 0) - *retval = sizeof(struct proc_taskinfo); - } + error = copyout(&originator_info, buffer, size); + if (error == 0) + *retval = size; } break; - case PROC_PIDTASKALLINFO: { - struct proc_taskallinfo pall; + case PROC_PIDORIGINATOR_BGSTATE: { + uint32_t is_backgrounded; + error = proc_get_originatorbgstate(&is_backgrounded); + if (error) + goto out; - 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); - } + error = copyout(&is_backgrounded, buffer, size); + if (error == 0) + *retval = size; } break; - case PROC_PIDTHREADID64INFO: - thuniqueid = 1; - case PROC_PIDTHREADINFO:{ - struct proc_threadinfo pthinfo; + default: + error = ENOTSUP; + } +out: + return error; +} - 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; +/***************************** 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; - case PROC_PIDLISTTHREADS:{ - error = proc_pidlistthreads(p, buffer, buffersize, retval); - } + /* 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 = kalloc((vm_size_t)k_buffersize); + if (!coalinfo) { + error = ENOMEM; + goto out; + } + bzero(coalinfo, k_buffersize); + + 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) + kfree(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; + task_reference(task); + if (coalition_is_leader(task, COALITION_TYPE_JETSAM, &coal) == FALSE) { + /* current task is not a coalition leader: find the leader */ + task_deallocate(task); + 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, 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; + int thuniqueid = 0; + int uniqidversion = 0; + boolean_t check_same_user; + + switch (flavor) { + case PROC_PIDLISTFDS: + size = PROC_PIDLISTFD_SIZE; + if (buffer == (user_addr_t)0) + 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_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_PIDREGIONPATHINFO2: + size = PROC_PIDREGIONPATHINFO2_SIZE; + break; + case PROC_PIDREGIONPATHINFO3: + size = PROC_PIDREGIONPATHINFO3_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; + } + + /* Certain operations don't require privileges */ + switch (flavor) { + case PROC_PIDT_SHORTBSDINFO: + case PROC_PIDUNIQIDENTIFIERINFO: + case PROC_PIDPATHINFO: + case PROC_PIDCOALITIONINFO: + 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; + + 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; + 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) { + 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; + + 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 = 1; + 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_PIDLISTTHREADS:{ + error = proc_pidlistthreads(p, buffer, buffersize, retval); + } break; case PROC_PIDREGIONINFO:{ @@ -1154,6 +1800,16 @@ proc_pidinfo(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t bu } 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); } @@ -1173,12 +1829,6 @@ proc_pidinfo(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t bu break; case PROC_PIDPATHINFO: { - p = proc_find(pid); - if (p == PROC_NULL) { - error = ESRCH; - goto out; - } - gotref = 1; error = proc_pidpathinfo(p, arg, buffer, buffersize, retval); } break; @@ -1202,6 +1852,38 @@ proc_pidinfo(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t bu } break; + case PROC_PIDARCHINFO: { + struct proc_archinfo 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; + default: error = ENOTSUP; } @@ -1209,12 +1891,14 @@ proc_pidinfo(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t bu out: if (gotref) proc_rele(p); + else if (zombie) + proc_drop_zombref(p); return(error); } -int -pid_vnodeinfo(vnode_t vp, uint32_t vid, struct fileproc * fp, int closeonexec, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) +int +pid_vnodeinfo(vnode_t vp, uint32_t vid, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) { struct vnode_fdinfo vfi; int error= 0; @@ -1223,7 +1907,7 @@ pid_vnodeinfo(vnode_t vp, uint32_t vid, struct fileproc * fp, int closeonexec, u return(error); } bzero(&vfi, sizeof(struct vnode_fdinfo)); - fill_fileinfo(fp, closeonexec, &vfi.pfi); + fill_fileinfo(fp, proc, fd, &vfi.pfi); error = fill_vnodeinfo(vp, &vfi.pvi); vnode_put(vp); if (error == 0) { @@ -1234,8 +1918,8 @@ pid_vnodeinfo(vnode_t vp, uint32_t vid, struct fileproc * fp, int closeonexec, u return(error); } -int -pid_vnodeinfopath(vnode_t vp, uint32_t vid, struct fileproc * fp, int closeonexec, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) +int +pid_vnodeinfopath(vnode_t vp, uint32_t vid, struct fileproc * fp, proc_t proc, int fd, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) { struct vnode_fdinfowithpath vfip; int count, error= 0; @@ -1244,7 +1928,7 @@ pid_vnodeinfopath(vnode_t vp, uint32_t vid, struct fileproc * fp, int closeonexe return(error); } bzero(&vfip, sizeof(struct vnode_fdinfowithpath)); - fill_fileinfo(fp, closeonexec, &vfip.pfi); + fill_fileinfo(fp, proc, fd, &vfip.pfi); error = fill_vnodeinfo(vp, &vfip.pvip.vip_vi) ; if (error == 0) { count = MAXPATHLEN; @@ -1259,17 +1943,33 @@ pid_vnodeinfopath(vnode_t vp, uint32_t vid, struct fileproc * fp, int closeonexe return(error); } -void -fill_fileinfo(struct fileproc * fp, int closeonexec, struct proc_fileinfo * fproc) +void +fill_fileinfo(struct fileproc * fp, proc_t proc, int fd, struct proc_fileinfo * fproc) { - fproc->fi_openflags = fp->f_fglob->fg_flag; - fproc->fi_status = 0; - fproc->fi_offset = fp->f_fglob->fg_offset; - fproc->fi_type = fp->f_fglob->fg_type; - if (fp->f_fglob->fg_count) - fproc->fi_status |= PROC_FP_SHARED; - if (closeonexec != 0) + fproc->fi_openflags = fp->f_fglob->fg_flag; + fproc->fi_status = 0; + fproc->fi_offset = fp->f_fglob->fg_offset; + fproc->fi_type = FILEGLOB_DTYPE(fp->f_fglob); + if (fp->f_fglob->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; + } } @@ -1281,6 +1981,7 @@ fill_vnodeinfo(vnode_t vp, struct vnode_info *vinfo) struct stat64 sb; int error = 0; + bzero(&sb, sizeof(struct stat64)); context = vfs_context_create((vfs_context_t)0); error = vn_stat(vp, &sb, NULL, 1, context); (void)vfs_context_rele(context); @@ -1302,33 +2003,34 @@ out: } int -pid_socketinfo(socket_t so, struct fileproc *fp, int closeonexec, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) +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, closeonexec, &s.pfi); + 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, int closeonexec, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) +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, closeonexec, &pseminfo.pfi); + 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) @@ -1339,13 +2041,13 @@ pid_pseminfo(struct psemnode *psem, struct fileproc *fp, int closeonexec, user_ } int -pid_pshminfo(struct pshmnode *pshm, struct fileproc *fp, int closeonexec, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) +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, closeonexec, &pshminfo.pfi); + 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) @@ -1356,13 +2058,13 @@ pid_pshminfo(struct pshmnode *pshm, struct fileproc *fp, int closeonexec, user_ } int -pid_pipeinfo(struct pipe * p, struct fileproc *fp, int closeonexec, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) +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, closeonexec, &pipeinfo.pfi); + 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); @@ -1372,14 +2074,18 @@ pid_pipeinfo(struct pipe * p, struct fileproc *fp, int closeonexec, user_addr_ } int -pid_kqueueinfo(struct kqueue * kq, struct fileproc *fp, int closeonexec, user_addr_t buffer, __unused uint32_t buffersize, int32_t * retval) +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)); - - fill_fileinfo(fp, closeonexec, &kqinfo.pfi); + + /* 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) @@ -1390,7 +2096,7 @@ pid_kqueueinfo(struct kqueue * kq, struct fileproc *fp, int closeonexec, user_a } int -pid_atalkinfo(__unused struct atalk * at, __unused struct fileproc *fp, __unused int closeonexec, __unused user_addr_t buffer, __unused uint32_t buffersize, __unused int32_t * retval) +pid_atalkinfo(__unused struct atalk * at, __unused struct fileproc *fp, __unused proc_t proc, __unused int fd, __unused user_addr_t buffer, __unused uint32_t buffersize, __unused int32_t * retval) { return ENOTSUP; } @@ -1403,9 +2109,8 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer { proc_t p; int error = ENOTSUP; - struct fileproc * fp; + struct fileproc * fp = NULL; uint32_t size; - int closeonexec = 0; switch (flavor) { case PROC_PIDFDVNODEINFO: @@ -1429,6 +2134,11 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer 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; @@ -1445,10 +2155,10 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer error = ESRCH; goto out; } - /* Do we have permission to look into this ? */ - if ((error = proc_security_policy(p)) != 0) { + + /* 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: { @@ -1459,8 +2169,7 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer goto out1; } /* no need to be under the fdlock */ - closeonexec = p->p_fd->fd_ofileflags[fd] & UF_EXCLOSE; - error = pid_vnodeinfo(vp, vid, fp, closeonexec, buffer, buffersize, retval); + error = pid_vnodeinfo(vp, vid, fp, p, fd, buffer, buffersize, retval); } break; @@ -1473,8 +2182,7 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer } /* no need to be under the fdlock */ - closeonexec = p->p_fd->fd_ofileflags[fd] & UF_EXCLOSE; - error = pid_vnodeinfopath(vp, vid, fp, closeonexec, buffer, buffersize, retval); + error = pid_vnodeinfopath(vp, vid, fp, p, fd, buffer, buffersize, retval); } break; @@ -1485,8 +2193,7 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer goto out1; } /* no need to be under the fdlock */ - closeonexec = p->p_fd->fd_ofileflags[fd] & UF_EXCLOSE; - error = pid_socketinfo(so, fp, closeonexec, buffer, buffersize, retval); + error = pid_socketinfo(so, fp, p, fd, buffer, buffersize, retval); } break; @@ -1497,8 +2204,7 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer goto out1; } /* no need to be under the fdlock */ - closeonexec = p->p_fd->fd_ofileflags[fd] & UF_EXCLOSE; - error = pid_pseminfo(psem, fp, closeonexec, buffer, buffersize, retval); + error = pid_pseminfo(psem, fp, p, fd, buffer, buffersize, retval); } break; @@ -1509,8 +2215,7 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer goto out1; } /* no need to be under the fdlock */ - closeonexec = p->p_fd->fd_ofileflags[fd] & UF_EXCLOSE; - error = pid_pshminfo(pshm, fp, closeonexec, buffer, buffersize, retval); + error = pid_pshminfo(pshm, fp, p, fd, buffer, buffersize, retval); } break; @@ -1521,44 +2226,53 @@ proc_pidfdinfo(int pid, int flavor, int fd, user_addr_t buffer, uint32_t buffer goto out1; } /* no need to be under the fdlock */ - closeonexec = p->p_fd->fd_ofileflags[fd] & UF_EXCLOSE; - error = pid_pipeinfo(cpipe, fp, closeonexec, buffer, buffersize, retval); + error = pid_pipeinfo(cpipe, fp, p, fd, buffer, buffersize, retval); } break; case PROC_PIDFDKQUEUEINFO: { struct kqueue * kq; - if ((error = fp_getfkq(p, fd, &fp, &kq)) !=0) { + if (fd == -1) { + if ((kq = p->p_wqkqueue) == NULL) { + /* wqkqueue is initialized on-demand */ + error = 0; + break; + } + } else if ((error = fp_getfkq(p, fd, &fp, &kq)) != 0) { goto out1; } + /* no need to be under the fdlock */ - closeonexec = p->p_fd->fd_ofileflags[fd] & UF_EXCLOSE; - error = pid_kqueueinfo(kq, fp, closeonexec, buffer, buffersize, retval); + error = pid_kqueueinfo(kq, fp, p, fd, buffer, buffersize, retval); } break; -#if NETAT - case PROC_PIDFDATALKINFO: { - struct atalk * at; + case PROC_PIDFDKQUEUE_EXTINFO: { + struct kqueue * kq; - if ((error = fp_getfatalk(p, fd, &fp, &at)) !=0) { + if (fd == -1) { + if ((kq = p->p_wqkqueue) == NULL) { + /* wqkqueue is initialized on-demand */ + error = 0; + break; + } + } else if ((error = fp_getfkq(p, fd, &fp, &kq)) != 0) { goto out1; } - - /* no need to be under the fdlock */ - closeonexec = p->p_fd->fd_ofileflags[fd] & UF_EXCLOSE; - error = pid_atalkinfo(at, fp, closeonexec, buffer, buffersize, retval); + error = pid_kqueue_extinfo(p, kq, buffer, buffersize, retval); } break; -#endif /* NETAT */ + default: { error = EINVAL; goto out1; } } - fp_drop(p, fd, fp , 0); + if (fp) { + fp_drop(p, fd, fp , 0); + } out1 : proc_rele(p); out: @@ -1591,48 +2305,48 @@ proc_fileport_info(__unused mach_port_name_t name, case PROC_PIDFILEPORTVNODEPATHINFO: { vnode_t vp; - if (fg->fg_type != DTYPE_VNODE) { + if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) { error = ENOTSUP; break; } vp = (struct vnode *)fg->fg_data; - error = pid_vnodeinfopath(vp, vnode_vid(vp), fp, 0, + error = pid_vnodeinfopath(vp, vnode_vid(vp), fp, PROC_NULL, 0, fia->fia_buffer, fia->fia_buffersize, fia->fia_retval); } break; case PROC_PIDFILEPORTSOCKETINFO: { socket_t so; - if (fg->fg_type != DTYPE_SOCKET) { + if (FILEGLOB_DTYPE(fg) != DTYPE_SOCKET) { error = EOPNOTSUPP; break; } so = (socket_t)fg->fg_data; - error = pid_socketinfo(so, fp, 0, + 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 (fg->fg_type != DTYPE_PSXSHM) { + if (FILEGLOB_DTYPE(fg) != DTYPE_PSXSHM) { error = EBADF; /* ick - mirror fp_getfpshm */ break; } pshm = (struct pshmnode *)fg->fg_data; - error = pid_pshminfo(pshm, fp, 0, + 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 (fg->fg_type != DTYPE_PIPE) { + if (FILEGLOB_DTYPE(fg) != DTYPE_PIPE) { error = EBADF; /* ick - mirror fp_getfpipe */ break; } cpipe = (struct pipe *)fg->fg_data; - error = pid_pipeinfo(cpipe, fp, 0, + error = pid_pipeinfo(cpipe, fp, PROC_NULL, 0, fia->fia_buffer, fia->fia_buffersize, fia->fia_retval); } break; @@ -1654,7 +2368,7 @@ proc_pidfileportinfo(int pid, int flavor, mach_port_name_t name, uint32_t size; struct fileport_info_args fia; - /* fileport types are restricted by filetype_issendable() */ + /* fileport types are restricted by file_issendable() */ switch (flavor) { case PROC_PIDFILEPORTVNODEPATHINFO: @@ -1679,9 +2393,10 @@ proc_pidfileportinfo(int pid, int flavor, mach_port_name_t name, error = ESRCH; goto out; } - if ((error = proc_security_policy(p)) != 0) { + + /* 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; @@ -1697,20 +2412,40 @@ out: return (error); } -static int -proc_security_policy(proc_t p) +int +proc_security_policy(proc_t targetp, __unused int callnum, __unused int flavor, boolean_t check_same_user) { - kauth_cred_t my_cred; - uid_t uid; +#if CONFIG_MACF + int error = 0; - my_cred = kauth_cred_proc_ref(p); - uid = kauth_cred_getuid(my_cred) ; - kauth_cred_unref(&my_cred); - - if ((uid != kauth_cred_getuid(kauth_cred_get())) - && suser(kauth_cred_get(), (u_short *)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); } @@ -1738,6 +2473,9 @@ proc_setcontrol(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t if (pid != pself->p_pid) return(EINVAL); + /* Do we have permission to look into this? */ + if ((error = proc_security_policy(pself, PROC_INFO_CALL_SETCONTROL, flavor, NO_CHECK_SAME_USER))) + goto out; switch (flavor) { case PROC_SELFSET_PCONTROL: { @@ -1800,42 +2538,7 @@ out: return(error); } -void -proc_dirty_start(struct proc *p) -{ - proc_lock(p); - while (p->p_dirty & P_DIRTY_BUSY) { - msleep(&p->p_dirty, &p->p_mlock, 0, "proc_dirty_start", NULL); - } - p->p_dirty |= P_DIRTY_BUSY; - proc_unlock(p); -} - -void -proc_dirty_end(struct proc *p) -{ - proc_lock(p); - if (p->p_dirty & P_DIRTY_BUSY) { - p->p_dirty &= ~P_DIRTY_BUSY; - wakeup(&p->p_dirty); - } - proc_unlock(p); -} - -static boolean_t -proc_validate_track_flags(uint32_t pcontrol, struct proc *target_p) { - /* Check idle exit isn't specified independently */ - if ((pcontrol & PROC_DIRTY_TRACK_MASK) == PROC_DIRTY_ALLOW_IDLE_EXIT) { - return false; - } - - /* See that the process isn't marked for termination */ - if (target_p->p_dirty & P_DIRTY_TERMINATED) { - return false; - } - - return true; -} +#if CONFIG_MEMORYSTATUS int proc_dirtycontrol(int pid, int flavor, uint64_t arg, int32_t *retval) { @@ -1845,16 +2548,29 @@ proc_dirtycontrol(int pid, int flavor, uint64_t arg, int32_t *retval) { kauth_cred_t my_cred, target_cred; boolean_t self = FALSE; boolean_t child = FALSE; + boolean_t zombref = FALSE; pid_t selfpid; target_p = proc_find(pid); + if (target_p == PROC_NULL) { - return(ESRCH); + if (flavor == PROC_DIRTYCONTROL_GET) { + target_p = proc_find_zombref(pid); + zombref = 1; + } + + if (target_p == PROC_NULL) + return(ESRCH); + } - + my_cred = kauth_cred_get(); target_cred = kauth_cred_proc_ref(target_p); - + + /* Do we have permission to look into this? */ + if ((error = proc_security_policy(target_p, PROC_INFO_CALL_DIRTYCONTROL, flavor, NO_CHECK_SAME_USER))) + goto out; + selfpid = proc_selfpid(); if (pid == selfpid) { self = TRUE; @@ -1869,109 +2585,62 @@ proc_dirtycontrol(int pid, int flavor, uint64_t arg, int32_t *retval) { error = EPERM; goto out; } - - proc_dirty_start(target_p); - if (proc_validate_track_flags(pcontrol, target_p)) { - /* Cumulative, as per */ - target_p->p_dirty |= - ((pcontrol & PROC_DIRTY_TRACK) ? P_DIRTY_TRACK : 0) | - ((pcontrol & PROC_DIRTY_ALLOW_IDLE_EXIT) ? P_DIRTY_ALLOW_IDLE_EXIT : 0); -#if CONFIG_MEMORYSTATUS - if ((target_p->p_dirty & P_DIRTY_CAN_IDLE_EXIT) == P_DIRTY_CAN_IDLE_EXIT) { - memorystatus_on_track_dirty(pid, TRUE); - } -#endif - } else { - error = EINVAL; - } - - proc_dirty_end(target_p); + error = memorystatus_dirty_track(target_p, pcontrol); } break; - case PROC_DIRTYCONTROL_SET: { - boolean_t kill = false; - + case PROC_DIRTYCONTROL_SET: { /* Check privileges; use cansignal() here since the process could be terminated */ if (!cansignal(current_proc(), my_cred, target_p, SIGKILL, 0)) { error = EPERM; goto out; } - proc_dirty_start(target_p); - - if (!(target_p->p_dirty & P_DIRTY_TRACK)) { - /* Dirty tracking not enabled */ - error = EINVAL; - } else if (pcontrol && (target_p->p_dirty & P_DIRTY_TERMINATED)) { - /* - * Process is set to be terminated and we're attempting to mark it dirty. - * Set for termination and marking as clean is OK - see . - */ - error = EBUSY; - } else { - int flag = (self == TRUE) ? P_DIRTY : P_DIRTY_SHUTDOWN; - if (pcontrol && !(target_p->p_dirty & flag)) { - target_p->p_dirty |= flag; - } else if ((pcontrol == 0) && (target_p->p_dirty & flag)) { - if ((flag == P_DIRTY_SHUTDOWN) && (!target_p->p_dirty & P_DIRTY)) { - /* Clearing the dirty shutdown flag, and the process is otherwise clean - kill */ - target_p->p_dirty |= P_DIRTY_TERMINATED; - kill = true; - } else if ((flag == P_DIRTY) && (target_p->p_dirty & P_DIRTY_TERMINATED)) { - /* Kill previously terminated processes if set clean */ - kill = true; - } - target_p->p_dirty &= ~flag; - } else { - /* Already set */ - error = EALREADY; - } - } -#if CONFIG_MEMORYSTATUS - if ((error == 0) && ((target_p->p_dirty & P_DIRTY_CAN_IDLE_EXIT) == P_DIRTY_CAN_IDLE_EXIT)) { - memorystatus_on_dirty(pid, pcontrol ? TRUE : FALSE); - } -#endif - proc_dirty_end(target_p); - - if ((error == 0) && (kill == true)) { - psignal(target_p, SIGKILL); - } + error = memorystatus_dirty_set(target_p, self, pcontrol); } break; case PROC_DIRTYCONTROL_GET: { /* No permissions check - dirty state is freely available */ if (retval) { - proc_dirty_start(target_p); - - *retval = 0; - if (target_p->p_dirty & P_DIRTY_TRACK) { - *retval |= PROC_DIRTY_TRACKED; - if (target_p->p_dirty & P_DIRTY_ALLOW_IDLE_EXIT) { - *retval |= PROC_DIRTY_ALLOWS_IDLE_EXIT; - } - if (target_p->p_dirty & P_DIRTY) { - *retval |= PROC_DIRTY_IS_DIRTY; - } - } - - proc_dirty_end(target_p); + *retval = memorystatus_dirty_get(target_p); } else { error = EINVAL; } } break; + + case PROC_DIRTYCONTROL_CLEAR: { + /* Check privileges; use cansignal() here since the process could be terminated */ + if (!cansignal(current_proc(), my_cred, target_p, SIGKILL, 0)) { + error = EPERM; + goto out; + } + + error = memorystatus_dirty_clear(target_p, pcontrol); + } + break; } out: - proc_rele(target_p); - kauth_cred_unref(&target_cred); + if (zombref) + proc_drop_zombref(target_p); + else + proc_rele(target_p); + + kauth_cred_unref(&target_cred); return(error); } +#else + +int +proc_dirtycontrol(__unused int pid, __unused int flavor, __unused uint64_t arg, __unused int32_t *retval) { + return ENOTSUP; +} + +#endif /* CONFIG_MEMORYSTATUS */ /* * proc_terminate() provides support for sudden termination. @@ -2011,22 +2680,22 @@ proc_terminate(int pid, int32_t *retval) error = EPERM; goto out; } - - proc_dirty_start(p); - - p->p_dirty |= P_DIRTY_TERMINATED; - - if ((p->p_dirty & (P_DIRTY_TRACK|P_DIRTY_IS_DIRTY)) == P_DIRTY_TRACK) { - /* Clean; mark as terminated and issue SIGKILL */ - sig = SIGKILL; - } else { - /* Dirty, terminated, or state tracking is unsupported; issue SIGTERM to allow cleanup */ - sig = SIGTERM; + + /* Not allowed to sudden terminate yourself */ + if (p == current_proc()) { + error = EPERM; + goto out; } - proc_dirty_end(p); +#if CONFIG_MEMORYSTATUS + /* Determine requisite signal to issue */ + sig = memorystatus_on_terminate(p); +#else + sig = SIGTERM; +#endif - proc_removethrottle(p); + proc_set_task_policy(p->task, THREAD_NULL, TASK_POLICY_ATTRIBUTE, + TASK_POLICY_TERMINATED, TASK_POLICY_ENABLE); psignal(p, sig); *retval = sig; @@ -2037,26 +2706,6 @@ out: return error; } -void -proc_removethrottle(proc_t p) - -{ - /* remove throttled states in all threads; process is going to terminate soon */ - proc_lock(p); - - /* if already marked marked for proc_termiantion.. */ - if ((p->p_lflag & P_LPTERMINATE) != 0) { - proc_unlock(p); - return; - } - p->p_lflag |= P_LPTERMINATE; - proc_unlock(p); - - (void)proc_task_remove_throttle(p->task); - -} - - /* * copy stat64 structure into vinfo_stat structure. */ @@ -2088,3 +2737,134 @@ munge_vinfo_stat(struct stat64 *sbp, struct vinfo_stat *vsbp) vsbp->vst_qspare[0] = sbp->st_qspare[0]; vsbp->vst_qspare[1] = sbp->st_qspare[1]; } + +int +proc_pid_rusage(int pid, int flavor, user_addr_t buffer, __unused int32_t *retval) +{ + proc_t p; + int error; + int zombie = 0; + + if ((p = proc_find(pid)) == PROC_NULL) { + if ((p = proc_find_zombref(pid)) == PROC_NULL) { + return (ESRCH); + } + zombie = 1; + } + + /* Do we have permission to look into this? */ + if ((error = proc_security_policy(p, PROC_INFO_CALL_PIDRUSAGE, flavor, CHECK_SAME_USER))) + goto out; + + error = proc_get_rusage(p, flavor, buffer, zombie); + +out: + if (zombie) + proc_drop_zombref(p); + else + proc_rele(p); + + return (error); +} + +void +proc_archinfo(proc_t p, struct proc_archinfo *pai) +{ + proc_lock(p); + pai->p_cputype = p->p_cputype; + pai->p_cpusubtype = p->p_cpusubtype; + proc_unlock(p); +} + +void +proc_pidcoalitioninfo(proc_t p, struct proc_pidcoalitioninfo *ppci) +{ + bzero(ppci, sizeof(*ppci)); + proc_coalitionids(p, ppci->coalition_id); +} + + + +/* + * Wrapper to provide NOTE_EXIT_DETAIL and NOTE_EXITSTATUS + * It mimics the data that is typically captured by the + * EVFILT_PROC, NOTE_EXIT event mechanism. + * See filt_proc() in kern_event.c. + */ +int +proc_pidnoteexit(proc_t p, uint64_t flags, uint32_t *data) +{ + uint32_t exit_data = 0; + uint32_t exit_flags = (uint32_t)flags; + + proc_lock(p); + + /* + * Allow access to the parent of the exiting + * child or the parent debugger only. + */ + do { + pid_t selfpid = proc_selfpid(); + + if (p->p_ppid == selfpid) + break; /* parent => ok */ + + if ((p->p_lflag & P_LTRACED) != 0 && + (p->p_oppid == selfpid)) + break; /* parent-in-waiting => ok */ + + proc_unlock(p); + return (EACCES); + } while (0); + + if ((exit_flags & NOTE_EXITSTATUS) != 0) { + /* The signal and exit status */ + exit_data |= (p->p_xstat & NOTE_PDATAMASK); + } + + if ((exit_flags & NOTE_EXIT_DETAIL) != 0) { + /* The exit detail */ + if ((p->p_lflag & P_LTERM_DECRYPTFAIL) != 0) { + exit_data |= NOTE_EXIT_DECRYPTFAIL; + } + + if ((p->p_lflag & P_LTERM_JETSAM) != 0) { + exit_data |= NOTE_EXIT_MEMORY; + + switch (p->p_lflag & P_JETSAM_MASK) { + case P_JETSAM_VMPAGESHORTAGE: + exit_data |= NOTE_EXIT_MEMORY_VMPAGESHORTAGE; + break; + case P_JETSAM_VMTHRASHING: + exit_data |= NOTE_EXIT_MEMORY_VMTHRASHING; + break; + case P_JETSAM_FCTHRASHING: + exit_data |= NOTE_EXIT_MEMORY_FCTHRASHING; + break; + case P_JETSAM_VNODE: + exit_data |= NOTE_EXIT_MEMORY_VNODE; + break; + case P_JETSAM_HIWAT: + exit_data |= NOTE_EXIT_MEMORY_HIWAT; + break; + case P_JETSAM_PID: + exit_data |= NOTE_EXIT_MEMORY_PID; + break; + case P_JETSAM_IDLEEXIT: + exit_data |= NOTE_EXIT_MEMORY_IDLE; + break; + } + } + + if ((p->p_csflags & CS_KILLED) != 0) { + exit_data |= NOTE_EXIT_CSERROR; + } + } + + proc_unlock(p); + + *data = exit_data; + + return (0); +} +