]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
block, bfq: fix uaf for accessing waker_bfqq after splitting
authorYu Kuai <yukuai3@huawei.com>
Mon, 9 Sep 2024 13:41:48 +0000 (21:41 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 4 Oct 2024 14:32:32 +0000 (16:32 +0200)
[ Upstream commit 1ba0403ac6447f2d63914fb760c44a3b19c44eaf ]

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: Sasha Levin <sashal@kernel.org>
block/bfq-iosched.c

index 2b6fff39b9f4b41a6e5996c1e3e1cfc51c409673..2423e25fc23ae61be7ea009edc22d62d8fc3afb4 100644 (file)
@@ -6823,6 +6823,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
@@ -6884,7 +6909,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->bfqq_data[a_idx].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))
@@ -6904,7 +6929,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;
 
                                /*
@@ -6914,7 +6939,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);
                        }