Index: sys/kern/kern_proc.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_proc.c,v retrieving revision 1.210 diff -u -r1.210 kern_proc.c --- sys/kern/kern_proc.c 11 Mar 2018 15:13:05 -0000 1.210 +++ sys/kern/kern_proc.c 12 Mar 2018 18:53:38 -0000 @@ -1618,67 +1618,126 @@ } static int -sysctl_doeproc(SYSCTLFN_ARGS) +sysctl_doeproc_lookup(void *oldp, size_t *oldlenp, struct lwp *l, int type, + int op, int arg, u_int elem_size, u_int kelem_size, + u_int elem_count) { union { struct kinfo_proc kproc; struct kinfo_proc2 kproc2; } *kbuf; - struct proc *p, *next, *marker; + struct proc *p; char *where, *dp; - int type, op, arg, error; - u_int elem_size, kelem_size, elem_count; + int error; size_t buflen, needed; - bool match, zombie, mmmbrains; - - if (namelen == 1 && name[0] == CTL_QUERY) - return (sysctl_query(SYSCTLFN_CALL(rnode))); dp = where = oldp; buflen = where != NULL ? *oldlenp : 0; - error = 0; needed = 0; - type = rnode->sysctl_num; - if (type == KERN_PROC) { - if (namelen == 0) - return EINVAL; - switch (op = name[0]) { - case KERN_PROC_ALL: - if (namelen != 1) - return EINVAL; - arg = 0; - break; - default: - if (namelen != 2) - return EINVAL; - arg = name[1]; - break; + sysctl_unlock(); + + kbuf = kmem_alloc(sizeof(*kbuf), KM_SLEEP); + + mutex_enter(proc_lock); + p = proc_find_raw(arg); + + if (p == NULL || p->p_stat == SIDL) { + error = ESRCH; + goto out2; + } + + mutex_enter(p->p_lock); + error = kauth_authorize_process(l->l_cred, + KAUTH_PROCESS_CANSEE, p, + KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_ENTRY), NULL, NULL); + if (error != 0) + goto out3; + + if (buflen >= elem_size && + (type == KERN_PROC || elem_count > 0)) { + if (type == KERN_PROC) { + kbuf->kproc.kp_proc = *p; + fill_eproc(p, &kbuf->kproc.kp_eproc, false); + } else { + fill_kproc2(p, &kbuf->kproc2, false); + elem_count--; } - elem_count = 0; /* Hush little compiler, don't you cry */ - kelem_size = elem_size = sizeof(kbuf->kproc); + mutex_exit(p->p_lock); + mutex_exit(proc_lock); + /* + * Copy out elem_size, but not larger than kelem_size + */ + error = sysctl_copyout(l, kbuf, dp, + min(kelem_size, elem_size)); + if (error) + goto out1; + dp += elem_size; } else { - if (namelen != 4) - return EINVAL; - op = name[0]; - arg = name[1]; - elem_size = name[2]; - elem_count = name[3]; - kelem_size = sizeof(kbuf->kproc2); + mutex_exit(p->p_lock); + mutex_exit(proc_lock); } + needed += elem_size; + + if (where != NULL) { + *oldlenp = dp - where; + if (needed > *oldlenp) { + error = ENOMEM; + goto out1; + } + } else + *oldlenp = needed; + + kmem_free(kbuf, sizeof(*kbuf)); + + sysctl_relock(); + return 0; + out3: + mutex_exit(p->p_lock); + out2: + mutex_exit(proc_lock); + out1: + kmem_free(kbuf, sizeof(*kbuf)); + sysctl_relock(); + return error; +} + +static int +sysctl_doeproc_range(void *oldp, size_t *oldlenp, struct lwp *l, int type, + int op, int arg, u_int elem_size, u_int kelem_size, + u_int elem_count) +{ + union { + struct kinfo_proc kproc; + struct kinfo_proc2 kproc2; + } *kbuf; + struct proc *p, *next, *marker, *marker0; + char *where, *dp; + int error; + size_t buflen, needed; + bool match, zombie, mmmbrains; + + dp = where = oldp; + buflen = where != NULL ? *oldlenp : 0; + needed = 0; + match = false; sysctl_unlock(); kbuf = kmem_alloc(sizeof(*kbuf), KM_SLEEP); + marker0 = kmem_alloc(sizeof(*marker0), KM_SLEEP); + marker0->p_flag = PK_MARKER; marker = kmem_alloc(sizeof(*marker), KM_SLEEP); marker->p_flag = PK_MARKER; mutex_enter(proc_lock); mmmbrains = false; + LIST_INSERT_HEAD(&zombproc, marker0, p_list); + for (p = LIST_FIRST(&allproc);; p = next) { if (p == NULL) { if (!mmmbrains) { - p = LIST_FIRST(&zombproc); + p = LIST_NEXT(marker0, p_list); mmmbrains = true; } if (p == NULL) @@ -1708,11 +1767,6 @@ * do by session. */ switch (op) { - case KERN_PROC_PID: - /* could do this with just a lookup */ - match = (p->p_pid == (pid_t)arg); - break; - case KERN_PROC_PGRP: /* could do this by traversing pgrp */ match = (p->p_pgrp->pg_id == (pid_t)arg); @@ -1760,11 +1814,6 @@ match = true; /* allow everything */ break; - - default: - error = EINVAL; - mutex_exit(p->p_lock); - goto cleanup; } if (!match) { mutex_exit(p->p_lock); @@ -1821,6 +1870,7 @@ next = LIST_NEXT(p, p_list); } } + LIST_REMOVE(marker0, p_list); mutex_exit(proc_lock); if (where != NULL) { @@ -1833,10 +1883,9 @@ needed += KERN_PROCSLOP; *oldlenp = needed; } - if (kbuf) - kmem_free(kbuf, sizeof(*kbuf)); - if (marker) - kmem_free(marker, sizeof(*marker)); + kmem_free(kbuf, sizeof(*kbuf)); + kmem_free(marker0, sizeof(*marker0)); + kmem_free(marker, sizeof(*marker)); sysctl_relock(); return 0; bah: @@ -1844,17 +1893,76 @@ LIST_REMOVE(marker, p_list); else rw_exit(&p->p_reflock); - cleanup: + LIST_REMOVE(marker0, p_list); mutex_exit(proc_lock); out: - if (kbuf) - kmem_free(kbuf, sizeof(*kbuf)); - if (marker) - kmem_free(marker, sizeof(*marker)); + kmem_free(kbuf, sizeof(*kbuf)); + kmem_free(marker0, sizeof(*marker0)); + kmem_free(marker, sizeof(*marker)); sysctl_relock(); return error; } +static int +sysctl_doeproc(SYSCTLFN_ARGS) +{ + union { + struct kinfo_proc kproc; + struct kinfo_proc2 kproc2; + } *kbuf; + int type, op, arg; + u_int elem_size, kelem_size, elem_count; + + if (namelen == 1 && name[0] == CTL_QUERY) + return (sysctl_query(SYSCTLFN_CALL(rnode))); + + type = rnode->sysctl_num; + + if (type == KERN_PROC) { + if (namelen == 0) + return EINVAL; + switch (op = name[0]) { + case KERN_PROC_ALL: + if (namelen != 1) + return EINVAL; + arg = 0; + break; + default: + if (namelen != 2) + return EINVAL; + arg = name[1]; + break; + } + elem_count = 0; /* Hush little compiler, don't you cry */ + kelem_size = elem_size = sizeof(kbuf->kproc); + } else { + if (namelen != 4) + return EINVAL; + op = name[0]; + arg = name[1]; + elem_size = name[2]; + elem_count = name[3]; + kelem_size = sizeof(kbuf->kproc2); + } + + switch (op) { + case KERN_PROC_PID: + return sysctl_doeproc_lookup(oldp, oldlenp, l, type, op, arg, elem_size, kelem_size, elem_count); + case KERN_PROC_PGRP: + /* could do this by traversing pgrp */ + case KERN_PROC_SESSION: + case KERN_PROC_TTY: + case KERN_PROC_UID: + case KERN_PROC_RUID: + case KERN_PROC_GID: + case KERN_PROC_RGID: + case KERN_PROC_ALL: + return sysctl_doeproc_range(oldp, oldlenp, l, type, op, arg, elem_size, kelem_size, elem_count); + default: + return EINVAL; + } +} + int copyin_psstrings(struct proc *p, struct ps_strings *arginfo) {