release 6.14.3
This commit is contained in:
@@ -1,207 +0,0 @@
|
||||
From 6e7ac63c4c4a8fe7c66f856f4091d9b20899f167 Mon Sep 17 00:00:00 2001
|
||||
From: Bernd Schubert <bschubert@ddn.com>
|
||||
Date: Tue, 25 Mar 2025 18:29:31 +0100
|
||||
Subject: fuse: {io-uring} Fix a possible req cancellation race
|
||||
|
||||
task-A (application) might be in request_wait_answer and
|
||||
try to remove the request when it has FR_PENDING set.
|
||||
|
||||
task-B (a fuse-server io-uring task) might handle this
|
||||
request with FUSE_IO_URING_CMD_COMMIT_AND_FETCH, when
|
||||
fetching the next request and accessed the req from
|
||||
the pending list in fuse_uring_ent_assign_req().
|
||||
That code path was not protected by fiq->lock and so
|
||||
might race with task-A.
|
||||
|
||||
For scaling reasons we better don't use fiq->lock, but
|
||||
add a handler to remove canceled requests from the queue.
|
||||
|
||||
This also removes usage of fiq->lock from
|
||||
fuse_uring_add_req_to_ring_ent() altogether, as it was
|
||||
there just to protect against this race and incomplete.
|
||||
|
||||
Also added is a comment why FR_PENDING is not cleared.
|
||||
|
||||
Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support")
|
||||
Cc: <stable@vger.kernel.org> # v6.14
|
||||
Reported-by: Joanne Koong <joannelkoong@gmail.com>
|
||||
Closes: https://lore.kernel.org/all/CAJnrk1ZgHNb78dz-yfNTpxmW7wtT88A=m-zF0ZoLXKLUHRjNTw@mail.gmail.com/
|
||||
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
|
||||
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
||||
---
|
||||
fs/fuse/dev.c | 34 +++++++++++++++++++++++++---------
|
||||
fs/fuse/dev_uring.c | 15 +++++++++++----
|
||||
fs/fuse/dev_uring_i.h | 6 ++++++
|
||||
fs/fuse/fuse_dev_i.h | 1 +
|
||||
fs/fuse/fuse_i.h | 3 +++
|
||||
5 files changed, 46 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/fs/fuse/dev.c
|
||||
+++ b/fs/fuse/dev.c
|
||||
@@ -407,6 +407,24 @@ static int queue_interrupt(struct fuse_r
|
||||
return 0;
|
||||
}
|
||||
|
||||
+bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock)
|
||||
+{
|
||||
+ spin_lock(lock);
|
||||
+ if (test_bit(FR_PENDING, &req->flags)) {
|
||||
+ /*
|
||||
+ * FR_PENDING does not get cleared as the request will end
|
||||
+ * up in destruction anyway.
|
||||
+ */
|
||||
+ list_del(&req->list);
|
||||
+ spin_unlock(lock);
|
||||
+ __fuse_put_request(req);
|
||||
+ req->out.h.error = -EINTR;
|
||||
+ return true;
|
||||
+ }
|
||||
+ spin_unlock(lock);
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
static void request_wait_answer(struct fuse_req *req)
|
||||
{
|
||||
struct fuse_conn *fc = req->fm->fc;
|
||||
@@ -428,22 +446,20 @@ static void request_wait_answer(struct f
|
||||
}
|
||||
|
||||
if (!test_bit(FR_FORCE, &req->flags)) {
|
||||
+ bool removed;
|
||||
+
|
||||
/* Only fatal signals may interrupt this */
|
||||
err = wait_event_killable(req->waitq,
|
||||
test_bit(FR_FINISHED, &req->flags));
|
||||
if (!err)
|
||||
return;
|
||||
|
||||
- spin_lock(&fiq->lock);
|
||||
- /* Request is not yet in userspace, bail out */
|
||||
- if (test_bit(FR_PENDING, &req->flags)) {
|
||||
- list_del(&req->list);
|
||||
- spin_unlock(&fiq->lock);
|
||||
- __fuse_put_request(req);
|
||||
- req->out.h.error = -EINTR;
|
||||
+ if (test_bit(FR_URING, &req->flags))
|
||||
+ removed = fuse_uring_remove_pending_req(req);
|
||||
+ else
|
||||
+ removed = fuse_remove_pending_req(req, &fiq->lock);
|
||||
+ if (removed)
|
||||
return;
|
||||
- }
|
||||
- spin_unlock(&fiq->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
--- a/fs/fuse/dev_uring.c
|
||||
+++ b/fs/fuse/dev_uring.c
|
||||
@@ -726,8 +726,6 @@ static void fuse_uring_add_req_to_ring_e
|
||||
struct fuse_req *req)
|
||||
{
|
||||
struct fuse_ring_queue *queue = ent->queue;
|
||||
- struct fuse_conn *fc = req->fm->fc;
|
||||
- struct fuse_iqueue *fiq = &fc->iq;
|
||||
|
||||
lockdep_assert_held(&queue->lock);
|
||||
|
||||
@@ -737,9 +735,7 @@ static void fuse_uring_add_req_to_ring_e
|
||||
ent->state);
|
||||
}
|
||||
|
||||
- spin_lock(&fiq->lock);
|
||||
clear_bit(FR_PENDING, &req->flags);
|
||||
- spin_unlock(&fiq->lock);
|
||||
ent->fuse_req = req;
|
||||
ent->state = FRRS_FUSE_REQ;
|
||||
list_move(&ent->list, &queue->ent_w_req_queue);
|
||||
@@ -1238,6 +1234,8 @@ void fuse_uring_queue_fuse_req(struct fu
|
||||
if (unlikely(queue->stopped))
|
||||
goto err_unlock;
|
||||
|
||||
+ set_bit(FR_URING, &req->flags);
|
||||
+ req->ring_queue = queue;
|
||||
ent = list_first_entry_or_null(&queue->ent_avail_queue,
|
||||
struct fuse_ring_ent, list);
|
||||
if (ent)
|
||||
@@ -1276,6 +1274,8 @@ bool fuse_uring_queue_bq_req(struct fuse
|
||||
return false;
|
||||
}
|
||||
|
||||
+ set_bit(FR_URING, &req->flags);
|
||||
+ req->ring_queue = queue;
|
||||
list_add_tail(&req->list, &queue->fuse_req_bg_queue);
|
||||
|
||||
ent = list_first_entry_or_null(&queue->ent_avail_queue,
|
||||
@@ -1306,6 +1306,13 @@ bool fuse_uring_queue_bq_req(struct fuse
|
||||
return true;
|
||||
}
|
||||
|
||||
+bool fuse_uring_remove_pending_req(struct fuse_req *req)
|
||||
+{
|
||||
+ struct fuse_ring_queue *queue = req->ring_queue;
|
||||
+
|
||||
+ return fuse_remove_pending_req(req, &queue->lock);
|
||||
+}
|
||||
+
|
||||
static const struct fuse_iqueue_ops fuse_io_uring_ops = {
|
||||
/* should be send over io-uring as enhancement */
|
||||
.send_forget = fuse_dev_queue_forget,
|
||||
--- a/fs/fuse/dev_uring_i.h
|
||||
+++ b/fs/fuse/dev_uring_i.h
|
||||
@@ -142,6 +142,7 @@ void fuse_uring_abort_end_requests(struc
|
||||
int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags);
|
||||
void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req);
|
||||
bool fuse_uring_queue_bq_req(struct fuse_req *req);
|
||||
+bool fuse_uring_remove_pending_req(struct fuse_req *req);
|
||||
|
||||
static inline void fuse_uring_abort(struct fuse_conn *fc)
|
||||
{
|
||||
@@ -199,6 +200,11 @@ static inline bool fuse_uring_ready(stru
|
||||
{
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+static inline bool fuse_uring_remove_pending_req(struct fuse_req *req)
|
||||
+{
|
||||
+ return false;
|
||||
+}
|
||||
|
||||
#endif /* CONFIG_FUSE_IO_URING */
|
||||
|
||||
--- a/fs/fuse/fuse_dev_i.h
|
||||
+++ b/fs/fuse/fuse_dev_i.h
|
||||
@@ -61,6 +61,7 @@ int fuse_copy_out_args(struct fuse_copy_
|
||||
void fuse_dev_queue_forget(struct fuse_iqueue *fiq,
|
||||
struct fuse_forget_link *forget);
|
||||
void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req);
|
||||
+bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock);
|
||||
|
||||
#endif
|
||||
|
||||
--- a/fs/fuse/fuse_i.h
|
||||
+++ b/fs/fuse/fuse_i.h
|
||||
@@ -378,6 +378,7 @@ struct fuse_io_priv {
|
||||
* FR_FINISHED: request is finished
|
||||
* FR_PRIVATE: request is on private list
|
||||
* FR_ASYNC: request is asynchronous
|
||||
+ * FR_URING: request is handled through fuse-io-uring
|
||||
*/
|
||||
enum fuse_req_flag {
|
||||
FR_ISREPLY,
|
||||
@@ -392,6 +393,7 @@ enum fuse_req_flag {
|
||||
FR_FINISHED,
|
||||
FR_PRIVATE,
|
||||
FR_ASYNC,
|
||||
+ FR_URING,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -441,6 +443,7 @@ struct fuse_req {
|
||||
|
||||
#ifdef CONFIG_FUSE_IO_URING
|
||||
void *ring_entry;
|
||||
+ void *ring_queue;
|
||||
#endif
|
||||
};
|
||||
|
30
debian/patches/patchset-pf/fuse/0001-virtiofs-add-filesystem-context-source-name-check.patch
vendored
Normal file
30
debian/patches/patchset-pf/fuse/0001-virtiofs-add-filesystem-context-source-name-check.patch
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
From bd6633c0e527dbcf6b52d3b34b49a980b125c866 Mon Sep 17 00:00:00 2001
|
||||
From: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
|
||||
Date: Mon, 7 Apr 2025 19:50:49 +0800
|
||||
Subject: virtiofs: add filesystem context source name check
|
||||
|
||||
In certain scenarios, for example, during fuzz testing, the source
|
||||
name may be NULL, which could lead to a kernel panic. Therefore, an
|
||||
extra check for the source name should be added.
|
||||
|
||||
Fixes: a62a8ef9d97d ("virtio-fs: add virtiofs filesystem")
|
||||
Cc: <stable@vger.kernel.org> # all LTS kernels
|
||||
Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
|
||||
Link: https://lore.kernel.org/20250407115111.25535-1-xiangsheng.hou@mediatek.com
|
||||
Signed-off-by: Christian Brauner <brauner@kernel.org>
|
||||
---
|
||||
fs/fuse/virtio_fs.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/fs/fuse/virtio_fs.c
|
||||
+++ b/fs/fuse/virtio_fs.c
|
||||
@@ -1670,6 +1670,9 @@ static int virtio_fs_get_tree(struct fs_
|
||||
unsigned int virtqueue_size;
|
||||
int err = -EIO;
|
||||
|
||||
+ if (!fsc->source)
|
||||
+ return invalf(fsc, "No source specified");
|
||||
+
|
||||
/* This gets a reference on virtio_fs object. This ptr gets installed
|
||||
* in fc->iq->priv. Once fuse_conn is going away, it calls ->put()
|
||||
* to drop the reference to this object.
|
Reference in New Issue
Block a user