diff --git a/sys/vfs/procfs/procfs_map.c b/sys/vfs/procfs/procfs_map.c --- a/sys/vfs/procfs/procfs_map.c +++ b/sys/vfs/procfs/procfs_map.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -58,11 +59,12 @@ struct uio *uio) { struct proc *p = lp->lwp_proc; + struct vmspace *vm = p->p_vmspace; ssize_t buflen = uio->uio_offset + uio->uio_resid; struct vnode *vp; char *fullpath, *freepath; int error; - vm_map_t map = &p->p_vmspace->vm_map; + vm_map_t map; vm_map_entry_t entry; struct sbuf *sb = NULL; unsigned int last_timestamp; @@ -74,17 +76,41 @@ if (uio->uio_offset < 0 || uio->uio_resid < 0 || buflen >= INT_MAX) return EINVAL; + + /* + * Take a hold on the vmspace for the whole scan, mirroring + * procfs_rwmem()/procfs_mem.c. Without it, a concurrent exec/exit on + * the target can free the vmspace (and the embedded vm_map and its + * lock) while we drop the map read lock per iteration below, turning + * the cached `map` and per-entry `ba` into dangling pointers (DF-0923). + * + * p_token is held for the entire function (held on entry, not released + * here): this keeps lwkt-token release order strictly LIFO + * (vmspace_hold() acquires vm_map.token on top of p_token, so + * vm_map.token must be released first via vmspace_drop() below) and + * prevents exec from swapping p_vmspace before the hold lands. Note + * that exit() releases p_token around vmspace_relexit() (kern_exit.c), + * so the hold -- not p_token alone -- is what actually pins the + * vmspace. These checks run BEFORE the sbuf is allocated so the + * EFAULT paths do not leak it. + */ + if (p->p_stat == SIDL || p->p_stat == SZOMB) + return EFAULT; + if ((p->p_flags & (P_WEXIT | P_INEXEC)) || vmspace_getrefs(vm) < 0) + return EFAULT; + vmspace_hold(vm); + map = &vm->vm_map; + sb = sbuf_new (sb, NULL, buflen+1, 0); - if (sb == NULL) + if (sb == NULL) { + vmspace_drop(vm); return EIO; + } /* - * Lock the map so we can access it. Release the process token - * to avoid unnecessary token stalls while we are processing the - * map. + * Lock the map so we can access it. */ vm_map_lock_read(map); - lwkt_reltoken(&p->p_token); RB_FOREACH(entry, vm_map_rb_tree, &map->rb_root) { vm_map_backing_t ba; @@ -240,13 +266,18 @@ } } vm_map_unlock_read(map); + /* + * Release the vmspace hold taken above. Done before sbuf/uiomove so + * the vmspace stays valid for the whole scan (DF-0923). p_token is + * still held (never released), so vm_map.token is released in LIFO + * order. + */ + vmspace_drop(vm); if (sbuf_finish(sb) == 0) buflen = sbuf_len(sb); error = uiomove_frombuf(sbuf_data(sb), buflen, uio); sbuf_delete(sb); - lwkt_gettoken(&p->p_token); /* re-acquire */ - return error; }