All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups
@ 2022-04-01 10:27 Jan Kara
  2022-04-01 10:27 ` [PATCH 1/9] bfq: Avoid false marking of bic as stably merged Jan Kara
                   ` (10 more replies)
  0 siblings, 11 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara

Hello,

here is the seventh version of my patches to fix use-after-free issues in BFQ
when processes with merged queues get moved to different cgroups. Kuai has
confirmed that patches now fix all the issues his reproducer was able to
trigger so I've just added some tags, codewise this is the same as v6. Paolo,
can you please check whether the patches look good to you so that Jens can
merge them? Thanks!

Changes since v6:
* Added some Tested-by, Fixes, and CC tags

Changes since v5:
* Added handling of situation when bio is submitted for a cgroup that has
  already went through bfq_pd_offline()
* Convert bfq to avoid using deprecated __bio_blkcg() and thus fix possible
  races when returned cgroup can change while bfq is working with a request

Changes since v4:
* Even more aggressive splitting of merged bfq queues to avoid problems with
  long merge chains.

Changes since v3:
* Changed handling of bfq group move to handle the case when target of the
  merge has moved.

Changes since v2:
* Improved handling of bfq queue splitting on move between cgroups
* Removed broken change to bfq_put_cooperator()

Changes since v1:
* Added fix for bfq_put_cooperator()
* Added fix to handle move between cgroups in bfq_merge_bio()

								Honza
Previous versions:
Link: http://lore.kernel.org/r/20211223171425.3551-1-jack@suse.cz # v1
Link: http://lore.kernel.org/r/20220105143037.20542-1-jack@suse.cz # v2
Link: http://lore.kernel.org/r/20220112113529.6355-1-jack@suse.cz # v3
Link: http://lore.kernel.org/r/20220114164215.28972-1-jack@suse.cz # v4
Link: http://lore.kernel.org/r/20220121105503.14069-1-jack@suse.cz # v5
Link: http://lore.kernel.org/r/20220330123438.32719-1-jack@suse.cz # v6

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/9] bfq: Avoid false marking of bic as stably merged
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-18  1:34   ` Jens Axboe
  2022-04-01 10:27 ` [PATCH 2/9] bfq: Avoid merging queues with different parents Jan Kara
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

bfq_setup_cooperator() can mark bic as stably merged even though it
decides to not merge its bfqqs (when bfq_setup_merge() returns NULL).
Make sure to mark bic as stably merged only if we are really going to
merge bfqqs.

CC: stable@vger.kernel.org
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Fixes: 430a67f9d616 ("block, bfq: merge bursts of newly-created queues")
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-iosched.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 2e0dd68a3cbe..6d122c28086e 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -2895,9 +2895,12 @@ bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq,
 				struct bfq_queue *new_bfqq =
 					bfq_setup_merge(bfqq, stable_merge_bfqq);
 
-				bic->stably_merged = true;
-				if (new_bfqq && new_bfqq->bic)
-					new_bfqq->bic->stably_merged = true;
+				if (new_bfqq) {
+					bic->stably_merged = true;
+					if (new_bfqq->bic)
+						new_bfqq->bic->stably_merged =
+									true;
+				}
 				return new_bfqq;
 			} else
 				return NULL;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/9] bfq: Avoid merging queues with different parents
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
  2022-04-01 10:27 ` [PATCH 1/9] bfq: Avoid false marking of bic as stably merged Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-01 10:27 ` [PATCH 3/9] bfq: Split shared queues on move between cgroups Jan Kara
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

It can happen that the parent of a bfqq changes between the moment we
decide two queues are worth to merge (and set bic->stable_merge_bfqq)
and the moment bfq_setup_merge() is called. This can happen e.g. because
the process submitted IO for a different cgroup and thus bfqq got
reparented. It can even happen that the bfqq we are merging with has
parent cgroup that is already offline and going to be destroyed in which
case the merge can lead to use-after-free issues such as:

BUG: KASAN: use-after-free in __bfq_deactivate_entity+0x9cb/0xa50
Read of size 8 at addr ffff88800693c0c0 by task runc:[2:INIT]/10544

CPU: 0 PID: 10544 Comm: runc:[2:INIT] Tainted: G            E     5.15.2-0.g5fb85fd-default #1 openSUSE Tumbleweed (unreleased) f1f3b891c72369aebecd2e43e4641a6358867c70
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g155821a-rebuilt.opensuse.org 04/01/2014
Call Trace:
 <IRQ>
 dump_stack_lvl+0x46/0x5a
 print_address_description.constprop.0+0x1f/0x140
 ? __bfq_deactivate_entity+0x9cb/0xa50
 kasan_report.cold+0x7f/0x11b
 ? __bfq_deactivate_entity+0x9cb/0xa50
 __bfq_deactivate_entity+0x9cb/0xa50
 ? update_curr+0x32f/0x5d0
 bfq_deactivate_entity+0xa0/0x1d0
 bfq_del_bfqq_busy+0x28a/0x420
 ? resched_curr+0x116/0x1d0
 ? bfq_requeue_bfqq+0x70/0x70
 ? check_preempt_wakeup+0x52b/0xbc0
 __bfq_bfqq_expire+0x1a2/0x270
 bfq_bfqq_expire+0xd16/0x2160
 ? try_to_wake_up+0x4ee/0x1260
 ? bfq_end_wr_async_queues+0xe0/0xe0
 ? _raw_write_unlock_bh+0x60/0x60
 ? _raw_spin_lock_irq+0x81/0xe0
 bfq_idle_slice_timer+0x109/0x280
 ? bfq_dispatch_request+0x4870/0x4870
 __hrtimer_run_queues+0x37d/0x700
 ? enqueue_hrtimer+0x1b0/0x1b0
 ? kvm_clock_get_cycles+0xd/0x10
 ? ktime_get_update_offsets_now+0x6f/0x280
 hrtimer_interrupt+0x2c8/0x740

Fix the problem by checking that the parent of the two bfqqs we are
merging in bfq_setup_merge() is the same.

Link: https://lore.kernel.org/linux-block/20211125172809.GC19572@quack2.suse.cz/
CC: stable@vger.kernel.org
Fixes: 430a67f9d616 ("block, bfq: merge bursts of newly-created queues")
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-iosched.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 6d122c28086e..7d00b21ebe5d 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -2758,6 +2758,14 @@ bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq)
 	if (process_refs == 0 || new_process_refs == 0)
 		return NULL;
 
+	/*
+	 * Make sure merged queues belong to the same parent. Parents could
+	 * have changed since the time we decided the two queues are suitable
+	 * for merging.
+	 */
+	if (new_bfqq->entity.parent != bfqq->entity.parent)
+		return NULL;
+
 	bfq_log_bfqq(bfqq->bfqd, bfqq, "scheduling merge with queue %d",
 		new_bfqq->pid);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/9] bfq: Split shared queues on move between cgroups
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
  2022-04-01 10:27 ` [PATCH 1/9] bfq: Avoid false marking of bic as stably merged Jan Kara
  2022-04-01 10:27 ` [PATCH 2/9] bfq: Avoid merging queues with different parents Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-01 10:27 ` [PATCH 4/9] bfq: Update cgroup information before merging bio Jan Kara
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

When bfqq is shared by multiple processes it can happen that one of the
processes gets moved to a different cgroup (or just starts submitting IO
for different cgroup). In case that happens we need to split the merged
bfqq as otherwise we will have IO for multiple cgroups in one bfqq and
we will just account IO time to wrong entities etc.

Similarly if the bfqq is scheduled to merge with another bfqq but the
merge didn't happen yet, cancel the merge as it need not be valid
anymore.

CC: stable@vger.kernel.org
Fixes: e21b7a0b9887 ("block, bfq: add full hierarchical scheduling and cgroups support")
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-cgroup.c  | 36 +++++++++++++++++++++++++++++++++---
 block/bfq-iosched.c |  2 +-
 block/bfq-iosched.h |  1 +
 3 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c
index 420eda2589c0..9352f3cc2377 100644
--- a/block/bfq-cgroup.c
+++ b/block/bfq-cgroup.c
@@ -743,9 +743,39 @@ static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd,
 	}
 
 	if (sync_bfqq) {
-		entity = &sync_bfqq->entity;
-		if (entity->sched_data != &bfqg->sched_data)
-			bfq_bfqq_move(bfqd, sync_bfqq, bfqg);
+		if (!sync_bfqq->new_bfqq && !bfq_bfqq_coop(sync_bfqq)) {
+			/* We are the only user of this bfqq, just move it */
+			if (sync_bfqq->entity.sched_data != &bfqg->sched_data)
+				bfq_bfqq_move(bfqd, sync_bfqq, bfqg);
+		} else {
+			struct bfq_queue *bfqq;
+
+			/*
+			 * The queue was merged to a different queue. Check
+			 * that the merge chain still belongs to the same
+			 * cgroup.
+			 */
+			for (bfqq = sync_bfqq; bfqq; bfqq = bfqq->new_bfqq)
+				if (bfqq->entity.sched_data !=
+				    &bfqg->sched_data)
+					break;
+			if (bfqq) {
+				/*
+				 * Some queue changed cgroup so the merge is
+				 * not valid anymore. We cannot easily just
+				 * cancel the merge (by clearing new_bfqq) as
+				 * there may be other processes using this
+				 * queue and holding refs to all queues below
+				 * sync_bfqq->new_bfqq. Similarly if the merge
+				 * already happened, we need to detach from
+				 * bfqq now so that we cannot merge bio to a
+				 * request from the old cgroup.
+				 */
+				bfq_put_cooperator(sync_bfqq);
+				bfq_release_process_ref(bfqd, sync_bfqq);
+				bic_set_bfqq(bic, NULL, 1);
+			}
+		}
 	}
 
 	return bfqg;
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 7d00b21ebe5d..89fe3f85eb3c 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -5315,7 +5315,7 @@ static void bfq_put_stable_ref(struct bfq_queue *bfqq)
 	bfq_put_queue(bfqq);
 }
 
-static void bfq_put_cooperator(struct bfq_queue *bfqq)
+void bfq_put_cooperator(struct bfq_queue *bfqq)
 {
 	struct bfq_queue *__bfqq, *next;
 
diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h
index 3b83e3d1c2e5..a56763045d19 100644
--- a/block/bfq-iosched.h
+++ b/block/bfq-iosched.h
@@ -979,6 +979,7 @@ void bfq_weights_tree_remove(struct bfq_data *bfqd,
 void bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq,
 		     bool compensate, enum bfqq_expiration reason);
 void bfq_put_queue(struct bfq_queue *bfqq);
+void bfq_put_cooperator(struct bfq_queue *bfqq);
 void bfq_end_wr_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg);
 void bfq_release_process_ref(struct bfq_data *bfqd, struct bfq_queue *bfqq);
 void bfq_schedule_dispatch(struct bfq_data *bfqd);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/9] bfq: Update cgroup information before merging bio
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
                   ` (2 preceding siblings ...)
  2022-04-01 10:27 ` [PATCH 3/9] bfq: Split shared queues on move between cgroups Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-01 10:27 ` [PATCH 5/9] bfq: Drop pointless unlock-lock pair Jan Kara
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

When the process is migrated to a different cgroup (or in case of
writeback just starts submitting bios associated with a different
cgroup) bfq_merge_bio() can operate with stale cgroup information in
bic. Thus the bio can be merged to a request from a different cgroup or
it can result in merging of bfqqs for different cgroups or bfqqs of
already dead cgroups and causing possible use-after-free issues. Fix the
problem by updating cgroup information in bfq_merge_bio().

CC: stable@vger.kernel.org
Fixes: e21b7a0b9887 ("block, bfq: add full hierarchical scheduling and cgroups support")
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-iosched.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 89fe3f85eb3c..1fc4d4628fba 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -2457,10 +2457,17 @@ static bool bfq_bio_merge(struct request_queue *q, struct bio *bio,
 
 	spin_lock_irq(&bfqd->lock);
 
-	if (bic)
+	if (bic) {
+		/*
+		 * Make sure cgroup info is uptodate for current process before
+		 * considering the merge.
+		 */
+		bfq_bic_update_cgroup(bic, bio);
+
 		bfqd->bio_bfqq = bic_to_bfqq(bic, op_is_sync(bio->bi_opf));
-	else
+	} else {
 		bfqd->bio_bfqq = NULL;
+	}
 	bfqd->bio_bic = bic;
 
 	ret = blk_mq_sched_try_merge(q, bio, nr_segs, &free);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/9] bfq: Drop pointless unlock-lock pair
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
                   ` (3 preceding siblings ...)
  2022-04-01 10:27 ` [PATCH 4/9] bfq: Update cgroup information before merging bio Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-01 10:27 ` [PATCH 6/9] bfq: Remove pointless bfq_init_rq() calls Jan Kara
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

In bfq_insert_request() we unlock bfqd->lock only to call
trace_block_rq_insert() and then lock bfqd->lock again. This is really
pointless since tracing is disabled if we really care about performance
and even if the tracepoint is enabled, it is a quick call.

CC: stable@vger.kernel.org
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-iosched.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 1fc4d4628fba..19082e14f3c1 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -6150,11 +6150,8 @@ static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,
 		return;
 	}
 
-	spin_unlock_irq(&bfqd->lock);
-
 	trace_block_rq_insert(rq);
 
-	spin_lock_irq(&bfqd->lock);
 	bfqq = bfq_init_rq(rq);
 	if (!bfqq || at_head) {
 		if (at_head)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/9] bfq: Remove pointless bfq_init_rq() calls
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
                   ` (4 preceding siblings ...)
  2022-04-01 10:27 ` [PATCH 5/9] bfq: Drop pointless unlock-lock pair Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-01 10:27 ` [PATCH 7/9] bfq: Track whether bfq_group is still online Jan Kara
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

We call bfq_init_rq() from request merging functions where requests we
get should have already gone through bfq_init_rq() during insert and
anyway we want to do anything only if the request is already tracked by
BFQ. So replace calls to bfq_init_rq() with RQ_BFQQ() instead to simply
skip requests untracked by BFQ. We move bfq_init_rq() call in
bfq_insert_request() a bit earlier to cover request merging and thus
can transfer FIFO position in case of a merge.

CC: stable@vger.kernel.org
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-iosched.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 19082e14f3c1..d7cf930b47bb 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -2497,8 +2497,6 @@ static int bfq_request_merge(struct request_queue *q, struct request **req,
 	return ELEVATOR_NO_MERGE;
 }
 
-static struct bfq_queue *bfq_init_rq(struct request *rq);
-
 static void bfq_request_merged(struct request_queue *q, struct request *req,
 			       enum elv_merge type)
 {
@@ -2507,7 +2505,7 @@ static void bfq_request_merged(struct request_queue *q, struct request *req,
 	    blk_rq_pos(req) <
 	    blk_rq_pos(container_of(rb_prev(&req->rb_node),
 				    struct request, rb_node))) {
-		struct bfq_queue *bfqq = bfq_init_rq(req);
+		struct bfq_queue *bfqq = RQ_BFQQ(req);
 		struct bfq_data *bfqd;
 		struct request *prev, *next_rq;
 
@@ -2559,8 +2557,8 @@ static void bfq_request_merged(struct request_queue *q, struct request *req,
 static void bfq_requests_merged(struct request_queue *q, struct request *rq,
 				struct request *next)
 {
-	struct bfq_queue *bfqq = bfq_init_rq(rq),
-		*next_bfqq = bfq_init_rq(next);
+	struct bfq_queue *bfqq = RQ_BFQQ(rq),
+		*next_bfqq = RQ_BFQQ(next);
 
 	if (!bfqq)
 		goto remove;
@@ -6129,6 +6127,8 @@ static inline void bfq_update_insert_stats(struct request_queue *q,
 					   unsigned int cmd_flags) {}
 #endif /* CONFIG_BFQ_CGROUP_DEBUG */
 
+static struct bfq_queue *bfq_init_rq(struct request *rq);
+
 static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,
 			       bool at_head)
 {
@@ -6144,6 +6144,7 @@ static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,
 		bfqg_stats_update_legacy_io(q, rq);
 #endif
 	spin_lock_irq(&bfqd->lock);
+	bfqq = bfq_init_rq(rq);
 	if (blk_mq_sched_try_insert_merge(q, rq, &free)) {
 		spin_unlock_irq(&bfqd->lock);
 		blk_mq_free_requests(&free);
@@ -6152,7 +6153,6 @@ static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,
 
 	trace_block_rq_insert(rq);
 
-	bfqq = bfq_init_rq(rq);
 	if (!bfqq || at_head) {
 		if (at_head)
 			list_add(&rq->queuelist, &bfqd->dispatch);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 7/9] bfq: Track whether bfq_group is still online
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
                   ` (5 preceding siblings ...)
  2022-04-01 10:27 ` [PATCH 6/9] bfq: Remove pointless bfq_init_rq() calls Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-01 10:27 ` [PATCH 8/9] bfq: Get rid of __bio_blkcg() usage Jan Kara
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

Track whether bfq_group is still online. We cannot rely on
blkcg_gq->online because that gets cleared only after all policies are
offlined and we need something that gets updated already under
bfqd->lock when we are cleaning up our bfq_group to be able to guarantee
that when we see online bfq_group, it will stay online while we are
holding bfqd->lock lock.

CC: stable@vger.kernel.org
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-cgroup.c  | 3 ++-
 block/bfq-iosched.h | 2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c
index 9352f3cc2377..879380c2bc7e 100644
--- a/block/bfq-cgroup.c
+++ b/block/bfq-cgroup.c
@@ -557,6 +557,7 @@ static void bfq_pd_init(struct blkg_policy_data *pd)
 				   */
 	bfqg->bfqd = bfqd;
 	bfqg->active_entities = 0;
+	bfqg->online = true;
 	bfqg->rq_pos_tree = RB_ROOT;
 }
 
@@ -603,7 +604,6 @@ struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd,
 	struct bfq_entity *entity;
 
 	bfqg = bfq_lookup_bfqg(bfqd, blkcg);
-
 	if (unlikely(!bfqg))
 		return NULL;
 
@@ -979,6 +979,7 @@ static void bfq_pd_offline(struct blkg_policy_data *pd)
 
 put_async_queues:
 	bfq_put_async_queues(bfqd, bfqg);
+	bfqg->online = false;
 
 	spin_unlock_irqrestore(&bfqd->lock, flags);
 	/*
diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h
index a56763045d19..4664e2f3e828 100644
--- a/block/bfq-iosched.h
+++ b/block/bfq-iosched.h
@@ -928,6 +928,8 @@ struct bfq_group {
 
 	/* reference counter (see comments in bfq_bic_update_cgroup) */
 	int ref;
+	/* Is bfq_group still online? */
+	bool online;
 
 	struct bfq_entity entity;
 	struct bfq_sched_data sched_data;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 8/9] bfq: Get rid of __bio_blkcg() usage
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
                   ` (6 preceding siblings ...)
  2022-04-01 10:27 ` [PATCH 7/9] bfq: Track whether bfq_group is still online Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-01 10:27 ` [PATCH 9/9] bfq: Make sure bfqg for which we are queueing requests is online Jan Kara
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

BFQ usage of __bio_blkcg() is a relict from the past. Furthermore if bio
would not be associated with any blkcg, the usage of __bio_blkcg() in
BFQ is prone to races with the task being migrated between cgroups as
__bio_blkcg() calls at different places could return different blkcgs.

Convert BFQ to the new situation where bio->bi_blkg is initialized in
bio_set_dev() and thus practically always valid. This allows us to save
blkcg_gq lookup and noticeably simplify the code.

CC: stable@vger.kernel.org
Fixes: 0fe061b9f03c ("blkcg: fix ref count issue with bio_blkcg() using task_css")
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-cgroup.c  | 63 +++++++++++++++++----------------------------
 block/bfq-iosched.c | 11 +-------
 block/bfq-iosched.h |  3 +--
 3 files changed, 25 insertions(+), 52 deletions(-)

diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c
index 879380c2bc7e..32d2c2a47480 100644
--- a/block/bfq-cgroup.c
+++ b/block/bfq-cgroup.c
@@ -586,27 +586,11 @@ static void bfq_group_set_parent(struct bfq_group *bfqg,
 	entity->sched_data = &parent->sched_data;
 }
 
-static struct bfq_group *bfq_lookup_bfqg(struct bfq_data *bfqd,
-					 struct blkcg *blkcg)
+static void bfq_link_bfqg(struct bfq_data *bfqd, struct bfq_group *bfqg)
 {
-	struct blkcg_gq *blkg;
-
-	blkg = blkg_lookup(blkcg, bfqd->queue);
-	if (likely(blkg))
-		return blkg_to_bfqg(blkg);
-	return NULL;
-}
-
-struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd,
-				     struct blkcg *blkcg)
-{
-	struct bfq_group *bfqg, *parent;
+	struct bfq_group *parent;
 	struct bfq_entity *entity;
 
-	bfqg = bfq_lookup_bfqg(bfqd, blkcg);
-	if (unlikely(!bfqg))
-		return NULL;
-
 	/*
 	 * Update chain of bfq_groups as we might be handling a leaf group
 	 * which, along with some of its relatives, has not been hooked yet
@@ -623,8 +607,15 @@ struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd,
 			bfq_group_set_parent(curr_bfqg, parent);
 		}
 	}
+}
 
-	return bfqg;
+struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)
+{
+	struct blkcg_gq *blkg = bio->bi_blkg;
+
+	if (!blkg)
+		return bfqd->root_group;
+	return blkg_to_bfqg(blkg);
 }
 
 /**
@@ -714,25 +705,15 @@ void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq,
  * Move bic to blkcg, assuming that bfqd->lock is held; which makes
  * sure that the reference to cgroup is valid across the call (see
  * comments in bfq_bic_update_cgroup on this issue)
- *
- * NOTE: an alternative approach might have been to store the current
- * cgroup in bfqq and getting a reference to it, reducing the lookup
- * time here, at the price of slightly more complex code.
  */
-static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd,
-						struct bfq_io_cq *bic,
-						struct blkcg *blkcg)
+static void *__bfq_bic_change_cgroup(struct bfq_data *bfqd,
+				     struct bfq_io_cq *bic,
+				     struct bfq_group *bfqg)
 {
 	struct bfq_queue *async_bfqq = bic_to_bfqq(bic, 0);
 	struct bfq_queue *sync_bfqq = bic_to_bfqq(bic, 1);
-	struct bfq_group *bfqg;
 	struct bfq_entity *entity;
 
-	bfqg = bfq_find_set_group(bfqd, blkcg);
-
-	if (unlikely(!bfqg))
-		bfqg = bfqd->root_group;
-
 	if (async_bfqq) {
 		entity = &async_bfqq->entity;
 
@@ -784,20 +765,24 @@ static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd,
 void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio)
 {
 	struct bfq_data *bfqd = bic_to_bfqd(bic);
-	struct bfq_group *bfqg = NULL;
+	struct bfq_group *bfqg = bfq_bio_bfqg(bfqd, bio);
 	uint64_t serial_nr;
 
-	rcu_read_lock();
-	serial_nr = __bio_blkcg(bio)->css.serial_nr;
+	serial_nr = bfqg_to_blkg(bfqg)->blkcg->css.serial_nr;
 
 	/*
 	 * Check whether blkcg has changed.  The condition may trigger
 	 * spuriously on a newly created cic but there's no harm.
 	 */
 	if (unlikely(!bfqd) || likely(bic->blkcg_serial_nr == serial_nr))
-		goto out;
+		return;
 
-	bfqg = __bfq_bic_change_cgroup(bfqd, bic, __bio_blkcg(bio));
+	/*
+	 * New cgroup for this process. Make sure it is linked to bfq internal
+	 * cgroup hierarchy.
+	 */
+	bfq_link_bfqg(bfqd, bfqg);
+	__bfq_bic_change_cgroup(bfqd, bic, bfqg);
 	/*
 	 * Update blkg_path for bfq_log_* functions. We cache this
 	 * path, and update it here, for the following
@@ -850,8 +835,6 @@ void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio)
 	 */
 	blkg_path(bfqg_to_blkg(bfqg), bfqg->blkg_path, sizeof(bfqg->blkg_path));
 	bic->blkcg_serial_nr = serial_nr;
-out:
-	rcu_read_unlock();
 }
 
 /**
@@ -1469,7 +1452,7 @@ void bfq_end_wr_async(struct bfq_data *bfqd)
 	bfq_end_wr_async_queues(bfqd, bfqd->root_group);
 }
 
-struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd, struct blkcg *blkcg)
+struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)
 {
 	return bfqd->root_group;
 }
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index d7cf930b47bb..e47c75f1fa0f 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -5726,14 +5726,7 @@ static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd,
 	struct bfq_queue *bfqq;
 	struct bfq_group *bfqg;
 
-	rcu_read_lock();
-
-	bfqg = bfq_find_set_group(bfqd, __bio_blkcg(bio));
-	if (!bfqg) {
-		bfqq = &bfqd->oom_bfqq;
-		goto out;
-	}
-
+	bfqg = bfq_bio_bfqg(bfqd, bio);
 	if (!is_sync) {
 		async_bfqq = bfq_async_queue_prio(bfqd, bfqg, ioprio_class,
 						  ioprio);
@@ -5779,8 +5772,6 @@ static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd,
 
 	if (bfqq != &bfqd->oom_bfqq && is_sync && !respawn)
 		bfqq = bfq_do_or_sched_stable_merge(bfqd, bfqq, bic);
-
-	rcu_read_unlock();
 	return bfqq;
 }
 
diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h
index 4664e2f3e828..978ef5d6fe6a 100644
--- a/block/bfq-iosched.h
+++ b/block/bfq-iosched.h
@@ -1009,8 +1009,7 @@ void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq,
 void bfq_init_entity(struct bfq_entity *entity, struct bfq_group *bfqg);
 void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio);
 void bfq_end_wr_async(struct bfq_data *bfqd);
-struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd,
-				     struct blkcg *blkcg);
+struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio);
 struct blkcg_gq *bfqg_to_blkg(struct bfq_group *bfqg);
 struct bfq_group *bfqq_group(struct bfq_queue *bfqq);
 struct bfq_group *bfq_create_group_hierarchy(struct bfq_data *bfqd, int node);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 9/9] bfq: Make sure bfqg for which we are queueing requests is online
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
                   ` (7 preceding siblings ...)
  2022-04-01 10:27 ` [PATCH 8/9] bfq: Get rid of __bio_blkcg() usage Jan Kara
@ 2022-04-01 10:27 ` Jan Kara
  2022-04-15  5:03 ` [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Christoph Hellwig
  2022-04-26 14:29 ` Paolo Valente
  10 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2022-04-01 10:27 UTC (permalink / raw)
  To: Paolo Valente; +Cc: linux-block, Jens Axboe, yukuai (C), Jan Kara, stable

Bios queued into BFQ IO scheduler can be associated with a cgroup that
was already offlined. This may then cause insertion of this bfq_group
into a service tree. But this bfq_group will get freed as soon as last
bio associated with it is completed leading to use after free issues for
service tree users. Fix the problem by making sure we always operate on
online bfq_group. If the bfq_group associated with the bio is not
online, we pick the first online parent.

CC: stable@vger.kernel.org
Fixes: e21b7a0b9887 ("block, bfq: add full hierarchical scheduling and cgroups support")
Tested-by: "yukuai (C)" <yukuai3@huawei.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 block/bfq-cgroup.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c
index 32d2c2a47480..09574af83566 100644
--- a/block/bfq-cgroup.c
+++ b/block/bfq-cgroup.c
@@ -612,10 +612,19 @@ static void bfq_link_bfqg(struct bfq_data *bfqd, struct bfq_group *bfqg)
 struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)
 {
 	struct blkcg_gq *blkg = bio->bi_blkg;
+	struct bfq_group *bfqg;
 
-	if (!blkg)
-		return bfqd->root_group;
-	return blkg_to_bfqg(blkg);
+	while (blkg) {
+		bfqg = blkg_to_bfqg(blkg);
+		if (bfqg->online) {
+			bio_associate_blkg_from_css(bio, &blkg->blkcg->css);
+			return bfqg;
+		}
+		blkg = blkg->parent;
+	}
+	bio_associate_blkg_from_css(bio,
+				&bfqg_to_blkg(bfqd->root_group)->blkcg->css);
+	return bfqd->root_group;
 }
 
 /**
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
                   ` (8 preceding siblings ...)
  2022-04-01 10:27 ` [PATCH 9/9] bfq: Make sure bfqg for which we are queueing requests is online Jan Kara
@ 2022-04-15  5:03 ` Christoph Hellwig
  2022-04-26 14:29 ` Paolo Valente
  10 siblings, 0 replies; 13+ messages in thread
From: Christoph Hellwig @ 2022-04-15  5:03 UTC (permalink / raw)
  To: Jan Kara; +Cc: Paolo Valente, linux-block, Jens Axboe, yukuai (C)

I'm not really a blk-cgroup expert, but these do look good to me:

Reviewed-by: Christoph Hellwig <hch@lst.de>

(and I'd like to get this staged for other blk-cgroup changs I have
pending :))

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/9] bfq: Avoid false marking of bic as stably merged
  2022-04-01 10:27 ` [PATCH 1/9] bfq: Avoid false marking of bic as stably merged Jan Kara
@ 2022-04-18  1:34   ` Jens Axboe
  0 siblings, 0 replies; 13+ messages in thread
From: Jens Axboe @ 2022-04-18  1:34 UTC (permalink / raw)
  To: paolo.valente, jack; +Cc: yukuai3, linux-block, stable

On Fri, 1 Apr 2022 12:27:42 +0200, Jan Kara wrote:
> bfq_setup_cooperator() can mark bic as stably merged even though it
> decides to not merge its bfqqs (when bfq_setup_merge() returns NULL).
> Make sure to mark bic as stably merged only if we are really going to
> merge bfqqs.
> 
> 

Applied, thanks!

[1/9] bfq: Avoid false marking of bic as stably merged
      commit: 70456e5210f40ffdb8f6d905acfdcec5bd5fad9e
[2/9] bfq: Avoid merging queues with different parents
      commit: c1cee4ab36acef271be9101590756ed0c0c374d9
[3/9] bfq: Split shared queues on move between cgroups
      commit: 3bc5e683c67d94bd839a1da2e796c15847b51b69
[4/9] bfq: Update cgroup information before merging bio
      commit: ea591cd4eb270393810e7be01feb8fde6a34fbbe
[5/9] bfq: Drop pointless unlock-lock pair
      commit: fc84e1f941b91221092da5b3102ec82da24c5673
[6/9] bfq: Remove pointless bfq_init_rq() calls
      commit: 5f550ede5edf846ecc0067be1ba80514e6fe7f8e
[7/9] bfq: Track whether bfq_group is still online
      commit: 09f871868080c33992cd6a9b72a5ca49582578fa
[8/9] bfq: Get rid of __bio_blkcg() usage
      commit: 4e54a2493e582361adc3bfbf06c7d50d19d18837
[9/9] bfq: Make sure bfqg for which we are queueing requests is online
      commit: 075a53b78b815301f8d3dd1ee2cd99554e34f0dd

Best regards,
-- 
Jens Axboe



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups
  2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
                   ` (9 preceding siblings ...)
  2022-04-15  5:03 ` [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Christoph Hellwig
@ 2022-04-26 14:29 ` Paolo Valente
  10 siblings, 0 replies; 13+ messages in thread
From: Paolo Valente @ 2022-04-26 14:29 UTC (permalink / raw)
  To: Jan Kara; +Cc: linux-block, Jens Axboe, yukuai (C)



> Il giorno 1 apr 2022, alle ore 12:27, Jan Kara <jack@suse.cz> ha scritto:
> 
> Hello,
> 
> here is the seventh version of my patches to fix use-after-free issues in BFQ
> when processes with merged queues get moved to different cgroups. Kuai has
> confirmed that patches now fix all the issues his reproducer was able to
> trigger so I've just added some tags, codewise this is the same as v6. Paolo,
> can you please check whether the patches look good to you so that Jens can
> merge them?

I think this is not needed any longer :) At any rate, your patches do fix
an evident problem, in a correct way.

Thank you,
Paolo

> Thanks!
> 
> Changes since v6:
> * Added some Tested-by, Fixes, and CC tags
> 
> Changes since v5:
> * Added handling of situation when bio is submitted for a cgroup that has
>  already went through bfq_pd_offline()
> * Convert bfq to avoid using deprecated __bio_blkcg() and thus fix possible
>  races when returned cgroup can change while bfq is working with a request
> 
> Changes since v4:
> * Even more aggressive splitting of merged bfq queues to avoid problems with
>  long merge chains.
> 
> Changes since v3:
> * Changed handling of bfq group move to handle the case when target of the
>  merge has moved.
> 
> Changes since v2:
> * Improved handling of bfq queue splitting on move between cgroups
> * Removed broken change to bfq_put_cooperator()
> 
> Changes since v1:
> * Added fix for bfq_put_cooperator()
> * Added fix to handle move between cgroups in bfq_merge_bio()
> 
> 								Honza
> Previous versions:
> Link: http://lore.kernel.org/r/20211223171425.3551-1-jack@suse.cz # v1
> Link: http://lore.kernel.org/r/20220105143037.20542-1-jack@suse.cz # v2
> Link: http://lore.kernel.org/r/20220112113529.6355-1-jack@suse.cz # v3
> Link: http://lore.kernel.org/r/20220114164215.28972-1-jack@suse.cz # v4
> Link: http://lore.kernel.org/r/20220121105503.14069-1-jack@suse.cz # v5
> Link: http://lore.kernel.org/r/20220330123438.32719-1-jack@suse.cz # v6


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2022-04-26 14:29 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-01 10:27 [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Jan Kara
2022-04-01 10:27 ` [PATCH 1/9] bfq: Avoid false marking of bic as stably merged Jan Kara
2022-04-18  1:34   ` Jens Axboe
2022-04-01 10:27 ` [PATCH 2/9] bfq: Avoid merging queues with different parents Jan Kara
2022-04-01 10:27 ` [PATCH 3/9] bfq: Split shared queues on move between cgroups Jan Kara
2022-04-01 10:27 ` [PATCH 4/9] bfq: Update cgroup information before merging bio Jan Kara
2022-04-01 10:27 ` [PATCH 5/9] bfq: Drop pointless unlock-lock pair Jan Kara
2022-04-01 10:27 ` [PATCH 6/9] bfq: Remove pointless bfq_init_rq() calls Jan Kara
2022-04-01 10:27 ` [PATCH 7/9] bfq: Track whether bfq_group is still online Jan Kara
2022-04-01 10:27 ` [PATCH 8/9] bfq: Get rid of __bio_blkcg() usage Jan Kara
2022-04-01 10:27 ` [PATCH 9/9] bfq: Make sure bfqg for which we are queueing requests is online Jan Kara
2022-04-15  5:03 ` [PATCH 0/9 v7] bfq: Avoid use-after-free when moving processes between cgroups Christoph Hellwig
2022-04-26 14:29 ` Paolo Valente

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.