diff options
author | Ming Lei <ming.lei@redhat.com> | 2022-03-18 21:01:42 +0800 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2022-03-18 09:57:56 -0600 |
commit | ee37eddbfa9e0401f13a01691cf4bbbacd2d16c9 (patch) | |
tree | 8419fbd2e8d0b4206a577da2c46465318a37b13a /block/blk-throttle.c | |
parent | 572299f03afd676dd4e20669cdaf5ed0fe1379d4 (diff) |
block: avoid use-after-free on throttle data
In throtl_pending_timer_fn(), request queue is retrieved from throttle
data. And tg's pending timer is deleted synchronously when releasing the
associated blkg, at that time, throttle data may have been freed since
commit 1059699f87eb ("block: move blkcg initialization/destroy into disk
allocation/release handler") moves freeing q->td to disk_release() from
blk_release_queue(). So use-after-free on q->td in throtl_pending_timer_fn
can be triggered.
Fixes the issue by:
- do nothing in case that disk is released, when there isn't any bio to
dispatch
- retrieve request queue from blkg instead of throttle data for
non top-level pending timer.
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20220318130144.1066064-2-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block/blk-throttle.c')
-rw-r--r-- | block/blk-throttle.c | 12 |
1 files changed, 11 insertions, 1 deletions
diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a3b3ebc72dd4..880701d0106f 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1137,12 +1137,22 @@ static void throtl_pending_timer_fn(struct timer_list *t) struct throtl_service_queue *sq = from_timer(sq, t, pending_timer); struct throtl_grp *tg = sq_to_tg(sq); struct throtl_data *td = sq_to_td(sq); - struct request_queue *q = td->queue; struct throtl_service_queue *parent_sq; + struct request_queue *q; bool dispatched; int ret; + /* throtl_data may be gone, so figure out request queue by blkg */ + if (tg) + q = tg->pd.blkg->q; + else + q = td->queue; + spin_lock_irq(&q->queue_lock); + + if (!q->root_blkg) + goto out_unlock; + if (throtl_can_upgrade(td, NULL)) throtl_upgrade_state(td); |