Skip to content

Commit 09098e6

Browse files
bsberndMiklos Szeredi
authored andcommitted
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: c090c8a ("fuse: Add io-uring sqe commit and fetch support") Cc: <[email protected]> # v6.14 Reported-by: Joanne Koong <[email protected]> Closes: https://lore.kernel.org/all/CAJnrk1ZgHNb78dz-yfNTpxmW7wtT88A=m-zF0ZoLXKLUHRjNTw@mail.gmail.com/ Signed-off-by: Bernd Schubert <[email protected]> Reviewed-by: Joanne Koong <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 4701f33 commit 09098e6

File tree

5 files changed

+46
-13
lines changed

5 files changed

+46
-13
lines changed

fs/fuse/dev.c

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,24 @@ static int queue_interrupt(struct fuse_req *req)
407407
return 0;
408408
}
409409

410+
bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock)
411+
{
412+
spin_lock(lock);
413+
if (test_bit(FR_PENDING, &req->flags)) {
414+
/*
415+
* FR_PENDING does not get cleared as the request will end
416+
* up in destruction anyway.
417+
*/
418+
list_del(&req->list);
419+
spin_unlock(lock);
420+
__fuse_put_request(req);
421+
req->out.h.error = -EINTR;
422+
return true;
423+
}
424+
spin_unlock(lock);
425+
return false;
426+
}
427+
410428
static void request_wait_answer(struct fuse_req *req)
411429
{
412430
struct fuse_conn *fc = req->fm->fc;
@@ -428,22 +446,20 @@ static void request_wait_answer(struct fuse_req *req)
428446
}
429447

430448
if (!test_bit(FR_FORCE, &req->flags)) {
449+
bool removed;
450+
431451
/* Only fatal signals may interrupt this */
432452
err = wait_event_killable(req->waitq,
433453
test_bit(FR_FINISHED, &req->flags));
434454
if (!err)
435455
return;
436456

437-
spin_lock(&fiq->lock);
438-
/* Request is not yet in userspace, bail out */
439-
if (test_bit(FR_PENDING, &req->flags)) {
440-
list_del(&req->list);
441-
spin_unlock(&fiq->lock);
442-
__fuse_put_request(req);
443-
req->out.h.error = -EINTR;
457+
if (test_bit(FR_URING, &req->flags))
458+
removed = fuse_uring_remove_pending_req(req);
459+
else
460+
removed = fuse_remove_pending_req(req, &fiq->lock);
461+
if (removed)
444462
return;
445-
}
446-
spin_unlock(&fiq->lock);
447463
}
448464

449465
/*

fs/fuse/dev_uring.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -726,8 +726,6 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent,
726726
struct fuse_req *req)
727727
{
728728
struct fuse_ring_queue *queue = ent->queue;
729-
struct fuse_conn *fc = req->fm->fc;
730-
struct fuse_iqueue *fiq = &fc->iq;
731729

732730
lockdep_assert_held(&queue->lock);
733731

@@ -737,9 +735,7 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent,
737735
ent->state);
738736
}
739737

740-
spin_lock(&fiq->lock);
741738
clear_bit(FR_PENDING, &req->flags);
742-
spin_unlock(&fiq->lock);
743739
ent->fuse_req = req;
744740
ent->state = FRRS_FUSE_REQ;
745741
list_move(&ent->list, &queue->ent_w_req_queue);
@@ -1238,6 +1234,8 @@ void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req)
12381234
if (unlikely(queue->stopped))
12391235
goto err_unlock;
12401236

1237+
set_bit(FR_URING, &req->flags);
1238+
req->ring_queue = queue;
12411239
ent = list_first_entry_or_null(&queue->ent_avail_queue,
12421240
struct fuse_ring_ent, list);
12431241
if (ent)
@@ -1276,6 +1274,8 @@ bool fuse_uring_queue_bq_req(struct fuse_req *req)
12761274
return false;
12771275
}
12781276

1277+
set_bit(FR_URING, &req->flags);
1278+
req->ring_queue = queue;
12791279
list_add_tail(&req->list, &queue->fuse_req_bg_queue);
12801280

12811281
ent = list_first_entry_or_null(&queue->ent_avail_queue,
@@ -1306,6 +1306,13 @@ bool fuse_uring_queue_bq_req(struct fuse_req *req)
13061306
return true;
13071307
}
13081308

1309+
bool fuse_uring_remove_pending_req(struct fuse_req *req)
1310+
{
1311+
struct fuse_ring_queue *queue = req->ring_queue;
1312+
1313+
return fuse_remove_pending_req(req, &queue->lock);
1314+
}
1315+
13091316
static const struct fuse_iqueue_ops fuse_io_uring_ops = {
13101317
/* should be send over io-uring as enhancement */
13111318
.send_forget = fuse_dev_queue_forget,

fs/fuse/dev_uring_i.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ void fuse_uring_abort_end_requests(struct fuse_ring *ring);
142142
int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags);
143143
void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req);
144144
bool fuse_uring_queue_bq_req(struct fuse_req *req);
145+
bool fuse_uring_remove_pending_req(struct fuse_req *req);
145146

146147
static inline void fuse_uring_abort(struct fuse_conn *fc)
147148
{
@@ -200,6 +201,11 @@ static inline bool fuse_uring_ready(struct fuse_conn *fc)
200201
return false;
201202
}
202203

204+
static inline bool fuse_uring_remove_pending_req(struct fuse_req *req)
205+
{
206+
return false;
207+
}
208+
203209
#endif /* CONFIG_FUSE_IO_URING */
204210

205211
#endif /* _FS_FUSE_DEV_URING_I_H */

fs/fuse/fuse_dev_i.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ int fuse_copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args,
6161
void fuse_dev_queue_forget(struct fuse_iqueue *fiq,
6262
struct fuse_forget_link *forget);
6363
void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req);
64+
bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock);
6465

6566
#endif
6667

fs/fuse/fuse_i.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ struct fuse_io_priv {
378378
* FR_FINISHED: request is finished
379379
* FR_PRIVATE: request is on private list
380380
* FR_ASYNC: request is asynchronous
381+
* FR_URING: request is handled through fuse-io-uring
381382
*/
382383
enum fuse_req_flag {
383384
FR_ISREPLY,
@@ -392,6 +393,7 @@ enum fuse_req_flag {
392393
FR_FINISHED,
393394
FR_PRIVATE,
394395
FR_ASYNC,
396+
FR_URING,
395397
};
396398

397399
/**
@@ -441,6 +443,7 @@ struct fuse_req {
441443

442444
#ifdef CONFIG_FUSE_IO_URING
443445
void *ring_entry;
446+
void *ring_queue;
444447
#endif
445448
};
446449

0 commit comments

Comments
 (0)