Module Name:    src
Committed By:   hannken
Date:           Wed Jan 17 10:20:12 UTC 2024

Modified Files:
        src/sys/miscfs/procfs: procfs.h procfs_subr.c procfs_vfsops.c

Log Message:
Using the exechook to revoke procfs nodes is racy and may deadlock:

one thread runs doexechooks() -> procfs_revoke_vnodes() and wants to suspend
the file system for vgone(), while another thread runs a forced unmount,
has the file system suspended, tries to disestablish the exechook and
waits for doexechooks() to complete.

Establish/disestablish the exechook on module load/unload instead
mount/unmount and use the hashmap to access all procfs nodes for this pid.

May fix PR kern/57775 ""panic: unmount: dangling vnode" while umounting procfs"


To generate a diff of this commit:
cvs rdiff -u -r1.83 -r1.84 src/sys/miscfs/procfs/procfs.h
cvs rdiff -u -r1.116 -r1.117 src/sys/miscfs/procfs/procfs_subr.c
cvs rdiff -u -r1.112 -r1.113 src/sys/miscfs/procfs/procfs_vfsops.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/miscfs/procfs/procfs.h
diff -u src/sys/miscfs/procfs/procfs.h:1.83 src/sys/miscfs/procfs/procfs.h:1.84
--- src/sys/miscfs/procfs/procfs.h:1.83	Wed Jan 17 10:19:21 2024
+++ src/sys/miscfs/procfs/procfs.h	Wed Jan 17 10:20:12 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: procfs.h,v 1.83 2024/01/17 10:19:21 hannken Exp $	*/
+/*	$NetBSD: procfs.h,v 1.84 2024/01/17 10:20:12 hannken Exp $	*/
 
 /*
  * Copyright (c) 1993
@@ -192,7 +192,6 @@ procfs_fileno(pid_t _pid, pfstype _type,
 #define PROCFS_TYPE(type)	((type) % PFSlast)
 
 struct procfsmount {
-	void *pmnt_exechook;
 	int pmnt_flags;
 };
 
@@ -272,7 +271,6 @@ int procfs_dolimit(struct lwp *, struct 
     struct uio *);
 
 void procfs_hashrem(struct pfsnode *);
-void procfs_revoke_vnodes(struct proc *, void *);
 int procfs_getfp(struct pfsnode *, struct proc *, struct file **);
 
 /* functions to check whether or not files should be displayed */

Index: src/sys/miscfs/procfs/procfs_subr.c
diff -u src/sys/miscfs/procfs/procfs_subr.c:1.116 src/sys/miscfs/procfs/procfs_subr.c:1.117
--- src/sys/miscfs/procfs/procfs_subr.c:1.116	Sat May 23 23:42:43 2020
+++ src/sys/miscfs/procfs/procfs_subr.c	Wed Jan 17 10:20:12 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: procfs_subr.c,v 1.116 2020/05/23 23:42:43 ad Exp $	*/
+/*	$NetBSD: procfs_subr.c,v 1.117 2024/01/17 10:20:12 hannken Exp $	*/
 
 /*-
  * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -102,7 +102,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: procfs_subr.c,v 1.116 2020/05/23 23:42:43 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: procfs_subr.c,v 1.117 2024/01/17 10:20:12 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -358,57 +358,6 @@ vfs_findname(const vfs_namemap_t *nm, co
 	return (0);
 }
 
-static bool
-procfs_revoke_selector(void *arg, struct vnode *vp)
-{
-	struct proc *p = arg;
-	struct pfsnode *pfs;
-
-	KASSERT(mutex_owned(vp->v_interlock));
-
-	pfs = VTOPFS(vp);
-
-	return (pfs != NULL && pfs->pfs_pid == p->p_pid);
-}
-
-void
-procfs_revoke_vnodes(struct proc *p, void *arg)
-{
-	int error;
-	bool suspended;
-	struct vnode *vp;
-	struct vnode_iterator *marker;
-	struct mount *mp = (struct mount *)arg;
-
-	if (!(p->p_flag & PK_SUGID))
-		return;
-
-	suspended = false;
-	vfs_vnode_iterator_init(mp, &marker);
-
-	while ((vp = vfs_vnode_iterator_next(marker,
-	    procfs_revoke_selector, p)) != NULL) {
-		if (vrecycle(vp))
-			continue;
-		/* Vnode is busy, we have to suspend the mount for vgone(). */
-		while (! suspended) {
-			error = vfs_suspend(mp, 0);
-			if (error == 0) {
-				suspended = true;
-			} else if (error != EINTR && error != ERESTART) {
-				KASSERT(error == EOPNOTSUPP);
-				break;
-			}
-		}
-		vgone(vp);
-	}
-
-	if (suspended)
-		vfs_resume(mp);
-
-	vfs_vnode_iterator_destroy(marker);
-}
-
 bool
 procfs_use_linux_compat(struct mount *mp)
 {

Index: src/sys/miscfs/procfs/procfs_vfsops.c
diff -u src/sys/miscfs/procfs/procfs_vfsops.c:1.112 src/sys/miscfs/procfs/procfs_vfsops.c:1.113
--- src/sys/miscfs/procfs/procfs_vfsops.c:1.112	Wed Jan 17 10:19:21 2024
+++ src/sys/miscfs/procfs/procfs_vfsops.c	Wed Jan 17 10:20:12 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: procfs_vfsops.c,v 1.112 2024/01/17 10:19:21 hannken Exp $	*/
+/*	$NetBSD: procfs_vfsops.c,v 1.113 2024/01/17 10:20:12 hannken Exp $	*/
 
 /*
  * Copyright (c) 1993
@@ -76,7 +76,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: procfs_vfsops.c,v 1.112 2024/01/17 10:19:21 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: procfs_vfsops.c,v 1.113 2024/01/17 10:20:12 hannken Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_compat_netbsd.h"
@@ -88,6 +88,7 @@ __KERNEL_RCSID(0, "$NetBSD: procfs_vfsop
 #include <sys/dirent.h>
 #include <sys/file.h>
 #include <sys/filedesc.h>
+#include <sys/fstrans.h>
 #include <sys/kauth.h>
 #include <sys/kernel.h>
 #include <sys/module.h>
@@ -113,6 +114,7 @@ VFS_PROTOS(procfs);
 #define PROCFS_HASHSIZE	256
 
 static kauth_listener_t procfs_listener;
+static void *procfs_exechook;
 LIST_HEAD(hashhead, pfsnode);
 static u_long procfs_hashmask;
 static struct hashhead *procfs_hashtab;
@@ -188,7 +190,6 @@ procfs_mount(
 
 	error = set_statvfs_info(path, UIO_USERSPACE, "procfs", UIO_SYSSPACE,
 	    mp->mnt_op->vfs_name, mp, l);
-	pmnt->pmnt_exechook = exechook_establish(procfs_revoke_vnodes, mp);
 	if (*data_len >= sizeof *args)
 		pmnt->pmnt_flags = args->flags;
 	else
@@ -213,8 +214,6 @@ procfs_unmount(struct mount *mp, int mnt
 	if ((error = vflush(mp, 0, flags)) != 0)
 		return (error);
 
-	exechook_disestablish(VFSTOPROC(mp)->pmnt_exechook);
-
 	kmem_free(mp->mnt_data, sizeof(struct procfsmount));
 	mp->mnt_data = NULL;
 
@@ -513,6 +512,48 @@ struct vfsops procfs_vfsops = {
 	.vfs_opv_descs = procfs_vnodeopv_descs
 };
 
+static void
+procfs_exechook_cb(struct proc *p, void *arg)
+{
+	struct hashhead *head;
+	struct pfsnode *pfs;
+	struct mount *mp;
+	struct pfskey key;
+	struct vnode *vp;
+	int error;
+
+	if (!(p->p_flag & PK_SUGID))
+		return;
+
+	head = procfs_hashhead(p->p_pid);
+
+again:
+	mutex_enter(&procfs_hashlock);
+	LIST_FOREACH(pfs, head, pfs_hash) {
+		if (pfs->pfs_pid != p->p_pid)
+			continue;
+		mp = pfs->pfs_mount;
+		key = pfs->pfs_key;
+		vfs_ref(mp);
+		mutex_exit(&procfs_hashlock);
+
+		error = vcache_get(mp, &key, sizeof(key), &vp);
+		vfs_rele(mp);
+		if (error != 0)
+			goto again;
+		if (vrecycle(vp))
+			goto again;
+		do {
+			error = vfs_suspend(mp, 0);
+		} while (error == EINTR || error == ERESTART);
+		vgone(vp);
+		if (error == 0)
+			vfs_resume(mp);
+		goto again;
+	}
+	mutex_exit(&procfs_hashlock);
+}
+
 static int
 procfs_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
     void *arg0, void *arg1, void *arg2, void *arg3)
@@ -575,12 +616,21 @@ procfs_modcmd(modcmd_t cmd, void *arg)
 		procfs_listener = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
 		    procfs_listener_cb, NULL);
 
+		procfs_exechook = exechook_establish(procfs_exechook_cb, NULL);
+
+		mutex_init(&procfs_hashlock, MUTEX_DEFAULT, IPL_NONE);
+		procfs_hashtab = hashinit(PROCFS_HASHSIZE, HASH_LIST, true,
+		    &procfs_hashmask);
+
 		break;
 	case MODULE_CMD_FINI:
 		error = vfs_detach(&procfs_vfsops);
 		if (error != 0)
 			break;
 		kauth_unlisten_scope(procfs_listener);
+		exechook_disestablish(procfs_exechook);
+		mutex_destroy(&procfs_hashlock);
+		hashdone(procfs_hashtab, HASH_LIST, procfs_hashmask);
 		break;
 	default:
 		error = ENOTTY;

Reply via email to