--- a/sys/vfs/procfs/procfs_status.c +++ b/sys/vfs/procfs/procfs_status.c @@ -39,7 +39,10 @@ #include #include #include +#include #include + +MALLOC_DECLARE(M_PARGS); #include #include #include @@ -48,6 +51,8 @@ #include #include +MALLOC_DECLARE(M_PARGS); + #include #include #include @@ -186,11 +191,20 @@ int i; size_t bytes_left, done; size_t buflen; + struct pargs *pa; + char *snap; if (uio->uio_rw != UIO_READ) return (EOPNOTSUPP); /* + * Snapshot buffer. Allocated before any target pointers are + * captured so a blocking allocation cannot invalidate anything. + */ + snap = kmalloc(UPMAP_MAXPROCTITLE, M_TEMP, M_WAITOK); + pa = NULL; + + /* * If we are using the ps/cmdline caching, use that. Otherwise * revert back to the old way which only implements full cmdline * for the current process and just p->p_comm for all other @@ -215,6 +229,15 @@ if (bp[buflen] == 0) break; } + /* + * The scan above is non-blocking, but uiomove() below can + * block; blocking drops the p_token held by our caller + * (pfs_pfind) and lwp_userunmap() could then free the lpmap. + * Copy the title while we are guaranteed non-blocking. + */ + bcopy(bp, snap, buflen); + snap[buflen] = 0; + bp = snap; buf = NULL; } else if (p->p_upmap != NULL && p->p_upmap->proc_title[0] && (ps_argsopen || (CHECKIO(curp, p) && @@ -232,14 +255,31 @@ if (bp[buflen] == 0) break; } + /* same as above: proc_userunmap() vs blocking uiomove() */ + bcopy(bp, snap, buflen); + snap[buflen] = 0; + bp = snap; buf = NULL; } else if (p->p_args && (ps_argsopen || (CHECKIO(curp, p) && (p->p_flags & P_INEXEC) == 0 && !p_trespass(curp->p_ucred, p->p_ucred)) )) { - bp = p->p_args->ar_args; - buflen = p->p_args->ar_length; + /* + * execve() in the target can free p_args while uiomove() + * blocks (p_token is dropped on block). Hold a reference, + * exactly like sysctl_kern_proc_args() does, and copy the + * (small) arguments into our snapshot buffer. + */ + pa = p->p_args; + refcount_acquire(&pa->ar_ref); + buflen = pa->ar_length; + if (buflen <= UPMAP_MAXPROCTITLE) { + bcopy(pa->ar_args, snap, buflen); + bp = snap; + } else { + bp = pa->ar_args; /* reference keeps it alive */ + } buf = NULL; } else if (p != curp) { bp = p->p_comm; @@ -290,7 +330,13 @@ } error = uiomove_frombuf(bp, buflen, uio); + if (pa) { + if (refcount_release(&pa->ar_ref)) + kfree(pa, M_PARGS); + pa = NULL; + } if (buf) kfree(buf, M_TEMP); + kfree(snap, M_TEMP); return (error); }