From: Bernd Schubert Date: Mon, 8 Jun 2026 21:03:45 +0000 (+0200) Subject: fuse-uring: make a fuse_req on SQE commit only findable after memcpy X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1efd3d474fc0ba74dfd984249bca78807d739812;p=thirdparty%2Fkernel%2Flinux.git 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 Reported-by: Berkant Koc Fixes: c090c8abae4b6b ("fuse: Add io-uring sqe commit and fetch support") Cc: stable@kernel.org # 6.14 Signed-off-by: Bernd Schubert Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi --- diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index ed652034927ee..aae79d5a6588e 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);