Commit d16baa3f1453c14d680c5fee01cd122a22d0e0ce

Authored by Tejun Heo
Committed by Jens Axboe
1 parent 19cd3403cb

blk-iocost: fix NULL iocg deref from racing against initialization

When initializing iocost for a queue, its rqos should be registered before
the blkcg policy is activated to allow policy data initiailization to lookup
the associated ioc. This unfortunately means that the rqos methods can be
called on bios before iocgs are attached to all existing blkgs.

While the race is theoretically possible on ioc_rqos_throttle(), it mostly
happened in ioc_rqos_merge() due to the difference in how they lookup ioc.
The former determines it from the passed in @rqos and then bails before
dereferencing iocg if the looked up ioc is disabled, which most likely is
the case if initialization is still in progress. The latter looked up ioc by
dereferencing the possibly NULL iocg making it a lot more prone to actually
triggering the bug.

* Make ioc_rqos_merge() use the same method as ioc_rqos_throttle() to look
  up ioc for consistency.

* Make ioc_rqos_throttle() and ioc_rqos_merge() test for NULL iocg before
  dereferencing it.

* Explain the danger of NULL iocgs in blk_iocost_init().

Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Jonathan Lemon <bsd@fb.com>
Cc: stable@vger.kernel.org # v5.4+
Signed-off-by: Jens Axboe <axboe@kernel.dk>

Showing 1 changed file with 11 additions and 5 deletions Side-by-side Diff

... ... @@ -2551,8 +2551,8 @@
2551 2551 bool use_debt, ioc_locked;
2552 2552 unsigned long flags;
2553 2553  
2554   - /* bypass IOs if disabled or for root cgroup */
2555   - if (!ioc->enabled || !iocg->level)
  2554 + /* bypass IOs if disabled, still initializing, or for root cgroup */
  2555 + if (!ioc->enabled || !iocg || !iocg->level)
2556 2556 return;
2557 2557  
2558 2558 /* calculate the absolute vtime cost */
2559 2559  
... ... @@ -2679,14 +2679,14 @@
2679 2679 struct bio *bio)
2680 2680 {
2681 2681 struct ioc_gq *iocg = blkg_to_iocg(bio->bi_blkg);
2682   - struct ioc *ioc = iocg->ioc;
  2682 + struct ioc *ioc = rqos_to_ioc(rqos);
2683 2683 sector_t bio_end = bio_end_sector(bio);
2684 2684 struct ioc_now now;
2685 2685 u64 vtime, abs_cost, cost;
2686 2686 unsigned long flags;
2687 2687  
2688   - /* bypass if disabled or for root cgroup */
2689   - if (!ioc->enabled || !iocg->level)
  2688 + /* bypass if disabled, still initializing, or for root cgroup */
  2689 + if (!ioc->enabled || !iocg || !iocg->level)
2690 2690 return;
2691 2691  
2692 2692 abs_cost = calc_vtime_cost(bio, iocg, true);
... ... @@ -2863,6 +2863,12 @@
2863 2863 ioc_refresh_params(ioc, true);
2864 2864 spin_unlock_irq(&ioc->lock);
2865 2865  
  2866 + /*
  2867 + * rqos must be added before activation to allow iocg_pd_init() to
  2868 + * lookup the ioc from q. This means that the rqos methods may get
  2869 + * called before policy activation completion, can't assume that the
  2870 + * target bio has an iocg associated and need to test for NULL iocg.
  2871 + */
2866 2872 rq_qos_add(q, rqos);
2867 2873 ret = blkcg_activate_policy(q, &blkcg_policy_iocost);
2868 2874 if (ret) {