[Devel] [PATCH RHEL9 COMMIT] fs/fuse kio: add safety check in kpcs_dev_ioctl()

Konstantin Khorenko khorenko at virtuozzo.com
Thu Mar 27 14:11:07 MSK 2025


The commit is pushed to "branch-rh9-5.14.0-427.44.1.vz9.80.x-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh9-5.14.0-427.44.1.vz9.80.21
------>
commit 8fd436febdb4022fd5679e047b1f7bc4adfbe307
Author: Liu Kui <kui.liu at virtuozzo.com>
Date:   Fri Mar 21 09:59:11 2025 +0800

    fs/fuse kio: add safety check in kpcs_dev_ioctl()
    
    Apparently fc->kio.ctx needs to be checked before being used.
    However the check should be done in a way that can avoid a race
    condition between kpcs_dev_ioctl() and fuse_conn_destroy()
    where both can run concurrently.
    
    https://virtuozzo.atlassian.net/browse/VSTOR-102040
    Signed-off-by: Liu Kui <kui.liu at virtuozzo.com>
    
    Feature: fuse: kRPC - single RPC for kernel and userspace
---
 fs/fuse/inode.c                    |  4 +---
 fs/fuse/kio/pcs/pcs_cluster.c      |  4 ++++
 fs/fuse/kio/pcs/pcs_cluster.h      |  3 +++
 fs/fuse/kio/pcs/pcs_fuse_kdirect.c | 29 +++++++++++++++++++++++------
 4 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index b27422d1ee38..a22e0ffb3a8f 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -2221,10 +2221,8 @@ void fuse_conn_destroy(struct fuse_mount *fm)
 {
 	struct fuse_conn *fc = fm->fc;
 
-	if (fc->kio.op) { /* At this point all pending kio must be completed. */
+	if (fc->kio.op) /* At this point all pending kio must be completed. */
 		fc->kio.op->conn_fini(fm);
-		fc->kio.ctx = NULL;
-	}
 
 	fuse_abort_conn(fc);
 	fuse_wait_aborted(fc);
diff --git a/fs/fuse/kio/pcs/pcs_cluster.c b/fs/fuse/kio/pcs/pcs_cluster.c
index c87313b90ab3..710087c83fe6 100644
--- a/fs/fuse/kio/pcs/pcs_cluster.c
+++ b/fs/fuse/kio/pcs/pcs_cluster.c
@@ -603,6 +603,8 @@ int pcs_cluster_init(struct pcs_fuse_cluster *pfc, struct workqueue_struct *wq,
 
 	INIT_LIST_HEAD(&pfc->list);
 	pfc->fc = fc;
+	atomic_set(&pfc->refcnt, 1);
+	init_waitqueue_head(&pfc->waitq);
 
 	/* core init */
 	if (pcs_cc_init(&pfc->cc, wq, info->cluster_name, &attr))
@@ -617,6 +619,8 @@ int pcs_cluster_init(struct pcs_fuse_cluster *pfc, struct workqueue_struct *wq,
 
 void pcs_cluster_fini(struct pcs_fuse_cluster *pfc)
 {
+	if (!atomic_dec_and_test(&pfc->refcnt))
+		wait_event(pfc->waitq, atomic_read(&pfc->refcnt) == 0);
 	pcs_cc_fini(&pfc->cc);
 	kvfree(pfc);
 }
diff --git a/fs/fuse/kio/pcs/pcs_cluster.h b/fs/fuse/kio/pcs/pcs_cluster.h
index 8693d1bf38d7..914d1ad7865c 100644
--- a/fs/fuse/kio/pcs/pcs_cluster.h
+++ b/fs/fuse/kio/pcs/pcs_cluster.h
@@ -54,6 +54,9 @@ struct pcs_fuse_cluster {
 	struct list_head list;
 	struct pcs_cluster_core cc;
 	struct fuse_conn *fc;
+
+	atomic_t refcnt;
+	wait_queue_head_t waitq;
 };
 
 struct pcs_fuse_work {
diff --git a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
index 8da9550cc156..e3049ddaa091 100644
--- a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
+++ b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
@@ -296,17 +296,19 @@ static int kpcs_conn_init(struct fuse_mount *fm)
 static void kpcs_conn_fini(struct fuse_mount *fm)
 {
 	struct fuse_conn *fc = fm->fc;
+	struct pcs_fuse_cluster *pfc = READ_ONCE(fc->kio.ctx);
 
-	if (!fc->kio.ctx)
+	if (!pfc)
 		return;
-
 	TRACE("%s fc:%p\n", __FUNCTION__, fc);
-	unregister_client(fc->kio.ctx);
+
+	WRITE_ONCE(fc->kio.ctx, NULL);
+	unregister_client(pfc);
 	synchronize_rcu();
 	flush_workqueue(pcs_wq);
 	flush_workqueue(pcs_cpu_wq);
 	flush_workqueue(pcs_cleanup_wq);
-	pcs_cluster_fini((struct pcs_fuse_cluster *) fc->kio.ctx);
+	pcs_cluster_fini(pfc);
 
 	if (fc->ktrace)
 		fuse_ktrace_remove(fc);
@@ -1921,10 +1923,21 @@ static int kpcs_ioctl(struct file *file, struct inode *inode, unsigned int cmd,
 
 static int kpcs_dev_ioctl(struct fuse_conn *fc, unsigned int cmd, unsigned long arg, int len)
 {
-	struct pcs_fuse_cluster *pfc = fc->kio.ctx;
-	struct pcs_cluster_core *cc = &pfc->cc;
+	struct pcs_fuse_cluster *pfc;
+	struct pcs_cluster_core *cc;
 	int res;
 
+	rcu_read_lock();
+	pfc = READ_ONCE(fc->kio.ctx);
+	if (!pfc) {
+		rcu_read_unlock();
+		return -EINVAL;
+	}
+	atomic_inc(&pfc->refcnt);
+	rcu_read_unlock();
+
+	cc = &pfc->cc;
+
 	switch (cmd) {
 	case PCS_IOC_KRPC_CREATE:
 	{
@@ -2007,6 +2020,10 @@ static int kpcs_dev_ioctl(struct fuse_conn *fc, unsigned int cmd, unsigned long
 		res = -ENOIOCTLCMD;
 		break;
 	}
+
+	if (atomic_dec_and_test(&pfc->refcnt))
+		wake_up(&pfc->waitq);
+
 	return res;
 }
 


More information about the Devel mailing list