Commit 8a6bee34 authored by Yu Kuai's avatar Yu Kuai Committed by Zheng Zengkai
Browse files

blk-mq: fix potential uaf for 'queue_hw_ctx'

hulk inclusion
category: bugfix
bugzilla: 186389, https://gitee.com/openeuler/kernel/issues/I4Y43S


CVE: NA

--------------------------------

blk_mq_realloc_hw_ctxs() will free the 'queue_hw_ctx'(e.g. undate
submit_queues through configfs for null_blk), while it might still be
used from other context(e.g. switch elevator to none):

t1					t2
elevator_switch
 blk_mq_unquiesce_queue
  blk_mq_run_hw_queues
   queue_for_each_hw_ctx
    // assembly code for hctx = (q)->queue_hw_ctx[i]
    mov    0x48(%rbp),%rdx -> read old queue_hw_ctx

					__blk_mq_update_nr_hw_queues
					 blk_mq_realloc_hw_ctxs
					  hctxs = q->queue_hw_ctx
					  q->queue_hw_ctx = new_hctxs
					  kfree(hctxs)
    movslq %ebx,%rax
    mov    (%rdx,%rax,8),%rdi ->uaf

Sicne the queue is freezed in __blk_mq_update_nr_hw_queues(), fix the
problem by protecting 'queue_hw_ctx' through rcu where it can be accessed
without grabbing 'q_usage_counter'.

Signed-off-by: default avatarYu Kuai <yukuai3@huawei.com>
Reviewed-by: default avatarMing Lei <ming.lei@redhat.com>
Signed-off-by: default avatarZhang Wensheng <zhangwensheng5@huawei.com>
Reviewed-by: default avatarJason Yan <yanaijie@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent 8653cd5e
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -3280,7 +3280,15 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
		if (hctxs)
			memcpy(new_hctxs, hctxs, q->nr_hw_queues *
			       sizeof(*hctxs));
		q->queue_hw_ctx = new_hctxs;

		rcu_assign_pointer(q->queue_hw_ctx, new_hctxs);
		/*
		 * Make sure reading the old queue_hw_ctx from other
		 * context concurrently won't trigger uaf. and when
		 * it is in start up time, no need to sync rcu.
		 */
		if (hctxs)
			synchronize_rcu();
		kfree(hctxs);
		hctxs = new_hctxs;
	}
+12 −1
Original line number Diff line number Diff line
@@ -612,9 +612,20 @@ static inline void *blk_mq_rq_to_pdu(struct request *rq)
	return rq + 1;
}

static inline struct blk_mq_hw_ctx *queue_hctx(struct request_queue *q, int id)
{
	struct blk_mq_hw_ctx *hctx;

	rcu_read_lock();
	hctx = *(rcu_dereference(q->queue_hw_ctx) + id);
	rcu_read_unlock();

	return hctx;
}

#define queue_for_each_hw_ctx(q, hctx, i)				\
	for ((i) = 0; (i) < (q)->nr_hw_queues &&			\
	     ({ hctx = (q)->queue_hw_ctx[i]; 1; }); (i)++)
	     ({ hctx = queue_hctx((q), i); 1; }); (i)++)

#define hctx_for_each_ctx(hctx, ctx, i)					\
	for ((i) = 0; (i) < (hctx)->nr_ctx &&				\
+1 −1
Original line number Diff line number Diff line
@@ -421,7 +421,7 @@ struct request_queue {
	unsigned int		queue_depth;

	/* hw dispatch queues */
	struct blk_mq_hw_ctx	**queue_hw_ctx;
	struct blk_mq_hw_ctx __rcu	**queue_hw_ctx;
	unsigned int		nr_hw_queues;

	struct backing_dev_info	*backing_dev_info;