From: Ming Lei <ming.lei@redhat.com>
To: Yu Kuai <yukuai3@huawei.com>
Cc: axboe@kernel.dk, josef@toxicpanda.com, hch@infradead.org,
linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
nbd@other.debian.org, yi.zhang@huawei.com
Subject: Re: [PATCH v6 6/6] nbd: fix uaf in nbd_handle_reply()
Date: Wed, 15 Sep 2021 16:28:58 +0800 [thread overview]
Message-ID: <YUGuykbPt+Oxt2nk@T590> (raw)
In-Reply-To: <20210915081537.1684327-7-yukuai3@huawei.com>
On Wed, Sep 15, 2021 at 04:15:37PM +0800, Yu Kuai wrote:
> There is a problem that nbd_handle_reply() might access freed request:
>
> 1) At first, a normal io is submitted and completed with scheduler:
>
> internel_tag = blk_mq_get_tag -> get tag from sched_tags
> blk_mq_rq_ctx_init
> sched_tags->rq[internel_tag] = sched_tag->static_rq[internel_tag]
> ...
> blk_mq_get_driver_tag
> __blk_mq_get_driver_tag -> get tag from tags
> tags->rq[tag] = sched_tag->static_rq[internel_tag]
>
> So, both tags->rq[tag] and sched_tags->rq[internel_tag] are pointing
> to the request: sched_tags->static_rq[internal_tag]. Even if the
> io is finished.
>
> 2) nbd server send a reply with random tag directly:
>
> recv_work
> nbd_handle_reply
> blk_mq_tag_to_rq(tags, tag)
> rq = tags->rq[tag]
>
> 3) if the sched_tags->static_rq is freed:
>
> blk_mq_sched_free_requests
> blk_mq_free_rqs(q->tag_set, hctx->sched_tags, i)
> -> step 2) access rq before clearing rq mapping
> blk_mq_clear_rq_mapping(set, tags, hctx_idx);
> __free_pages() -> rq is freed here
>
> 4) Then, nbd continue to use the freed request in nbd_handle_reply
>
> Fix the problem by get 'q_usage_counter' before blk_mq_tag_to_rq(),
> thus request is ensured not to be freed because 'q_usage_counter' is
> not zero.
>
> Signed-off-by: Yu Kuai <yukuai3@huawei.com>
> ---
> block/blk-core.c | 1 +
> drivers/block/nbd.c | 19 ++++++++++++++++++-
> 2 files changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/block/blk-core.c b/block/blk-core.c
> index 5454db2fa263..2008e6903166 100644
> --- a/block/blk-core.c
> +++ b/block/blk-core.c
> @@ -489,6 +489,7 @@ void blk_queue_exit(struct request_queue *q)
> {
> percpu_ref_put(&q->q_usage_counter);
> }
> +EXPORT_SYMBOL(blk_queue_exit);
>
> static void blk_queue_usage_counter_release(struct percpu_ref *ref)
> {
> diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
> index 9a7bbf8ebe74..f065afcc7586 100644
> --- a/drivers/block/nbd.c
> +++ b/drivers/block/nbd.c
> @@ -824,6 +824,7 @@ static void recv_work(struct work_struct *work)
> work);
> struct nbd_device *nbd = args->nbd;
> struct nbd_config *config = nbd->config;
> + struct request_queue *q = nbd->disk->queue;
> struct nbd_sock *nsock;
> struct nbd_cmd *cmd;
> struct request *rq;
> @@ -834,13 +835,29 @@ static void recv_work(struct work_struct *work)
> if (nbd_read_reply(nbd, args->index, &reply))
> break;
>
> + /*
> + * Get q_usage_counter can prevent accessing freed request
> + * through blk_mq_tag_to_rq() in nbd_handle_reply(). If
> + * q_usage_counter is zero, then no request is inflight, which
> + * means something is wrong since we expect to find a request to
> + * complete here.
> + */
> + if (!percpu_ref_tryget(&q->q_usage_counter)) {
> + dev_err(disk_to_dev(nbd->disk), "%s: no io inflight\n",
> + __func__);
> + break;
> + }
> +
> cmd = nbd_handle_reply(nbd, args->index, &reply);
> - if (IS_ERR(cmd))
> + if (IS_ERR(cmd)) {
> + blk_queue_exit(q);
> break;
> + }
>
> rq = blk_mq_rq_from_pdu(cmd);
> if (likely(!blk_should_fake_timeout(rq->q)))
> blk_mq_complete_request(rq);
> + blk_queue_exit(q);
You can simply call percpu_ref_put() directly just like what scsi_end_request()
is doing.
--
Ming
next prev parent reply other threads:[~2021-09-15 8:29 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-09-15 8:15 [PATCH v6 0/6] handle unexpected message from server Yu Kuai
2021-09-15 8:15 ` [PATCH v6 1/6] nbd: don't handle response without a corresponding request message Yu Kuai
2021-09-15 8:15 ` [PATCH v6 2/6] nbd: make sure request completion won't concurrent Yu Kuai
2021-09-15 8:15 ` [PATCH v6 3/6] nbd: check sock index in nbd_read_stat() Yu Kuai
2021-09-15 8:15 ` [PATCH v6 4/6] nbd: don't start request if nbd_queue_rq() failed Yu Kuai
2021-09-15 8:15 ` [PATCH v6 5/6] nbd: partition nbd_read_stat() into nbd_read_reply() and nbd_handle_reply() Yu Kuai
2021-09-15 8:15 ` [PATCH v6 6/6] nbd: fix uaf in nbd_handle_reply() Yu Kuai
2021-09-15 8:20 ` Christoph Hellwig
2021-09-15 8:44 ` yukuai (C)
2021-09-15 8:28 ` Ming Lei [this message]
2021-09-15 8:41 ` yukuai (C)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YUGuykbPt+Oxt2nk@T590 \
--to=ming.lei@redhat.com \
--cc=axboe@kernel.dk \
--cc=hch@infradead.org \
--cc=josef@toxicpanda.com \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nbd@other.debian.org \
--cc=yi.zhang@huawei.com \
--cc=yukuai3@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.