DragonFlyBSD Kernel Audit
DF-0923 / fix.diff
← back to finding ↓ download raw
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 <vm/vm.h>
 #include <sys/lock.h>
 #include <vm/pmap.h>
+#include <vm/vm_extern.h>
 #include <vm/vm_map.h>
 #include <vm/vm_page.h>
 #include <vm/vm_object.h>
@@ -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;
 }