summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYu Kuai <yukuai3@huawei.com>2024-09-09 16:41:48 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-10-17 16:12:01 +0300
commit63a07379fdb6c72450cb05294461c6016b8b7726 (patch)
treeaba7f5e397a8473f03e96981a01571a1c5372d14
parent40a6e660d2a3a7a5cb99f0b8ff4fb41bad039f68 (diff)
downloadlinux-63a07379fdb6c72450cb05294461c6016b8b7726.tar.xz
block, bfq: fix uaf for accessing waker_bfqq after splitting
commit 1ba0403ac6447f2d63914fb760c44a3b19c44eaf upstream. After commit 42c306ed7233 ("block, bfq: don't break merge chain in bfq_split_bfqq()"), if the current procress is the last holder of bfqq, the bfqq can be freed after bfq_split_bfqq(). Hence recored the bfqq and then access bfqq->waker_bfqq may trigger UAF. What's more, the waker_bfqq may in the merge chain of bfqq, hence just recored waker_bfqq is still not safe. Fix the problem by adding a helper bfq_waker_bfqq() to check if bfqq->waker_bfqq is in the merge chain, and current procress is the only holder. Fixes: 42c306ed7233 ("block, bfq: don't break merge chain in bfq_split_bfqq()") Signed-off-by: Yu Kuai <yukuai3@huawei.com> Link: https://lore.kernel.org/r/20240909134154.954924-2-yukuai1@huaweicloud.com Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--block/bfq-iosched.c31
1 files changed, 28 insertions, 3 deletions
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index ab16b4a66c42..b0bdb5197530 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -6568,6 +6568,31 @@ static void bfq_prepare_request(struct request *rq)
rq->elv.priv[0] = rq->elv.priv[1] = NULL;
}
+static struct bfq_queue *bfq_waker_bfqq(struct bfq_queue *bfqq)
+{
+ struct bfq_queue *new_bfqq = bfqq->new_bfqq;
+ struct bfq_queue *waker_bfqq = bfqq->waker_bfqq;
+
+ if (!waker_bfqq)
+ return NULL;
+
+ while (new_bfqq) {
+ if (new_bfqq == waker_bfqq) {
+ /*
+ * If waker_bfqq is in the merge chain, and current
+ * is the only procress.
+ */
+ if (bfqq_process_refs(waker_bfqq) == 1)
+ return NULL;
+ break;
+ }
+
+ new_bfqq = new_bfqq->new_bfqq;
+ }
+
+ return waker_bfqq;
+}
+
/*
* If needed, init rq, allocate bfq data structures associated with
* rq, and increment reference counters in the destination bfq_queue
@@ -6628,7 +6653,7 @@ static struct bfq_queue *bfq_init_rq(struct request *rq)
/* If the queue was seeky for too long, break it apart. */
if (bfq_bfqq_coop(bfqq) && bfq_bfqq_split_coop(bfqq) &&
!bic->stably_merged) {
- struct bfq_queue *old_bfqq = bfqq;
+ struct bfq_queue *waker_bfqq = bfq_waker_bfqq(bfqq);
/* Update bic before losing reference to bfqq */
if (bfq_bfqq_in_large_burst(bfqq))
@@ -6647,7 +6672,7 @@ static struct bfq_queue *bfq_init_rq(struct request *rq)
bfqq_already_existing = true;
if (!bfqq_already_existing) {
- bfqq->waker_bfqq = old_bfqq->waker_bfqq;
+ bfqq->waker_bfqq = waker_bfqq;
bfqq->tentative_waker_bfqq = NULL;
/*
@@ -6657,7 +6682,7 @@ static struct bfq_queue *bfq_init_rq(struct request *rq)
* woken_list of the waker. See
* bfq_check_waker for details.
*/
- if (bfqq->waker_bfqq)
+ if (waker_bfqq)
hlist_add_head(&bfqq->woken_list_node,
&bfqq->waker_bfqq->woken_list);
}