aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernd Schubert <bernd@bsbernd.com>2026-06-08 23:03:45 +0200
committerMiklos Szeredi <mszeredi@redhat.com>2026-06-15 14:06:14 +0200
commit1efd3d474fc0ba74dfd984249bca78807d739812 (patch)
tree200b29af05943cae5c0446ef518fb30be18abab6
parentb70a3aca16934c196f92abb17b01c1647b9bb63c (diff)
fuse-uring: make a fuse_req on SQE commit only findable after memcpy
Bad userspace might try to trick us and send commit SQEs request unique / commit-id of requests that are not even send to fuse-server (io_uring_cmd_done() not called) yet. fuse_uring_commit_fetch() ends the fuse request when the ring entry has a wrong state, but that could have caused a use-after-free with the memcpy operations in fuse_uring_send_in_task(). In order to avoid such races the call of fuse_uring_add_to_pq() is moved after the copy operations and just before completing the io-uring request - malicious userspace cannot find the request anymore until all prepration work in fuse-client/kernel is completed. This also moves fuse_uring_add_to_pq() a bit up in the code to avoid a forward declaration. Also not with a preparation commit, to make it easier to back port to older kernels. Reported-by: xlabai <xlabai@tencent.com> Reported-by: Berkant Koc <me@berkoc.com> Fixes: c090c8abae4b6b ("fuse: Add io-uring sqe commit and fetch support") Cc: stable@kernel.org # 6.14 Signed-off-by: Bernd Schubert <bernd@bsbernd.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/dev_uring.c33
1 files changed, 19 insertions, 14 deletions
diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index ed652034927e..aae79d5a6588 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -717,6 +717,19 @@ static int fuse_uring_prepare_send(struct fuse_ring_ent *ent,
return err;
}
+/* Used to find the request on SQE commit */
+static void fuse_uring_add_to_pq(struct fuse_ring_ent *ent)
+{
+ struct fuse_ring_queue *queue = ent->queue;
+ struct fuse_pqueue *fpq = &queue->fpq;
+ unsigned int hash;
+ struct fuse_req *req = ent->fuse_req;
+
+ req->ring_entry = ent;
+ hash = fuse_req_hash(req->in.h.unique);
+ list_move_tail(&req->list, &fpq->processing[hash]);
+}
+
/*
* Write data to the ring buffer and send the request to userspace,
* userspace will read it
@@ -739,6 +752,7 @@ static int fuse_uring_send_next_to_ring(struct fuse_ring_ent *ent,
ent->cmd = NULL;
ent->state = FRRS_USERSPACE;
list_move_tail(&ent->list, &queue->ent_in_userspace);
+ fuse_uring_add_to_pq(ent);
spin_unlock(&queue->lock);
io_uring_cmd_done(cmd, 0, issue_flags);
@@ -756,19 +770,6 @@ static void fuse_uring_ent_avail(struct fuse_ring_ent *ent,
ent->state = FRRS_AVAILABLE;
}
-/* Used to find the request on SQE commit */
-static void fuse_uring_add_to_pq(struct fuse_ring_ent *ent,
- struct fuse_req *req)
-{
- struct fuse_ring_queue *queue = ent->queue;
- struct fuse_pqueue *fpq = &queue->fpq;
- unsigned int hash;
-
- req->ring_entry = ent;
- hash = fuse_req_hash(req->in.h.unique);
- list_move_tail(&req->list, &fpq->processing[hash]);
-}
-
/*
* Assign a fuse queue entry to the given entry
*/
@@ -786,10 +787,13 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent,
}
clear_bit(FR_PENDING, &req->flags);
+
+ /* Until fuse_uring_add_to_pq() the req is not attached to any list */
+ list_del_init(&req->list);
+
ent->fuse_req = req;
ent->state = FRRS_FUSE_REQ;
list_move_tail(&ent->list, &queue->ent_w_req_queue);
- fuse_uring_add_to_pq(ent, req);
}
/* Fetch the next fuse request if available */
@@ -1220,6 +1224,7 @@ static void fuse_uring_send(struct fuse_ring_ent *ent, struct io_uring_cmd *cmd,
ent->state = FRRS_USERSPACE;
list_move_tail(&ent->list, &queue->ent_in_userspace);
ent->cmd = NULL;
+ fuse_uring_add_to_pq(ent);
spin_unlock(&queue->lock);
io_uring_cmd_done(cmd, ret, issue_flags);