[Devel] [PATCH VZ9] fs/fuse: avoid copying out data to killed requests.

Liu Kui kui.liu at virtuozzo.com
Tue Jan 7 15:55:10 MSK 2025


When a read request is killed, its buff pages would have been dropped,
copying data over would cause kernel crash due to invalid memory access.
Therefore we should not copying data over to a killed request.

Meanwhile if a read request is already under data write back IO, we
should wait for it to complete instead of killing it to avoid freeing
its buff pages.

Related to #VSTOR-96560

Signed-off-by: Liu Kui <kui.liu at virtuozzo.com>
---
 fs/fuse/dev.c   | 19 +++++++++----------
 fs/fuse/inode.c |  6 +++++-
 2 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 0fef877f731f..101bd4907828 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2017,6 +2017,9 @@ static int copy_out_splices(struct fuse_copy_state *cs, struct fuse_args *args,
 	struct page *dpage = ap->pages[0];
 	struct fd f = { .file = NULL };
 
+	if (args->out_numargs != 1 || !args->out_pages)
+		return -EINVAL;
+
 	nsplices = nbytes - sizeof(struct fuse_out_header);
 	if (nsplices & 3)
 		return -EINVAL;
@@ -2156,6 +2159,9 @@ static int copy_out_krpczc(struct fuse_copy_state *cs, struct fuse_args *args,
 	void *dst;
 	int err;
 
+	if (args->out_numargs != 1 || !args->out_pages)
+		return -EINVAL;
+
 	nchunks = nbytes - sizeof(struct fuse_out_header);
 
 	/* this is always at least one chunk */
@@ -2323,15 +2329,6 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
 		goto copy_finish;
 	}
 
-	if (oh.error == FUSE_OUT_SPLICES &&
-		oh.error == FUSE_OUT_KRPCZC) {
-		if (req->args->out_numargs != 1 || !req->args->out_pages) {
-			spin_unlock(&fpq->lock);
-			err = -EINVAL;
-			goto copy_finish;
-		}
-	}
-
 	clear_bit(FR_SENT, &req->flags);
 	list_move(&req->list, &fpq->io);
 	req->out.h = oh;
@@ -2341,7 +2338,9 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
 	if (!req->args->page_replace)
 		cs->move_pages = 0;
 
-	if (oh.error == FUSE_OUT_SPLICES) {
+	if (req->args->killed) {
+		err = -EINVAL;
+	} else if (oh.error == FUSE_OUT_SPLICES) {
 		req->out.h.error = 0;
 		err = copy_out_splices(cs, req->args, nbytes);
 	} else if (oh.error == FUSE_OUT_KRPCZC) {
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index cd0dbd74db33..d1456a5e1693 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -489,6 +489,11 @@ void fuse_kill_requests(struct fuse_conn *fc, struct inode *inode,
 			int i;
 
 			BUG_ON(req->in.h.opcode != FUSE_READ);
+
+			/* skip the request that is under data write back IO */
+			if (test_bit(FR_LOCKED, &req->flags) && req->out.h.unique)
+				continue;
+
 			req->args->killed = 1;
 
 			for (i = 0; i < ia->ap.num_pages; i++) {
@@ -600,7 +605,6 @@ int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid)
 				fuse_kill_requests(fc, inode, &fpq->processing[i]);
 			fuse_kill_requests(fc, inode, &fpq->io);
 			spin_unlock(&fpq->lock);
-
 		}
 
 		wake_up(&fi->page_waitq); /* readpage[s] can wait on fuse wb */
-- 
2.39.5 (Apple Git-154)



More information about the Devel mailing list